@topolo/cli 0.1.22 → 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.
- package/README.md +8 -2
- package/dist/index.js +1363 -340
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { platform as platform2 } from "os";
|
|
|
9
9
|
import { createInterface } from "readline/promises";
|
|
10
10
|
import { stdin as input } from "process";
|
|
11
11
|
|
|
12
|
-
//
|
|
12
|
+
// ../topolo-sdk/dist/index.js
|
|
13
13
|
function applyAuthHeaders(headers, credential) {
|
|
14
14
|
if (credential.kind === "api_key") {
|
|
15
15
|
headers.set("X-Api-Key", credential.apiKey);
|
|
@@ -67,6 +67,7 @@ var TopoloHttpError = class extends TopoloSdkError {
|
|
|
67
67
|
var SERVICES = {
|
|
68
68
|
agent: "https://agent-api.topolo.app",
|
|
69
69
|
auth: "https://auth.topolo.app",
|
|
70
|
+
backup: "https://backup.topolo.app",
|
|
70
71
|
books: "https://books.topolo.app",
|
|
71
72
|
bugfix: "https://bugfix.topolo.app",
|
|
72
73
|
bytes: "https://bytes-api.topolo.app",
|
|
@@ -77,7 +78,6 @@ var SERVICES = {
|
|
|
77
78
|
compose: "https://compose.topolo.app",
|
|
78
79
|
consent: "https://consent.topolo.app",
|
|
79
80
|
crm: "https://crm-api.topolo.app",
|
|
80
|
-
customer_success: "https://success.topolo.app",
|
|
81
81
|
developers: "https://developers.topolo.app",
|
|
82
82
|
director: "https://director.topolo.app",
|
|
83
83
|
feed: "https://feed-api.topolo.app",
|
|
@@ -91,9 +91,10 @@ var SERVICES = {
|
|
|
91
91
|
localize: "https://localize.topolo.app",
|
|
92
92
|
mail: "https://mail.topolo.app",
|
|
93
93
|
mdm: "https://mdm-api.topolo.app",
|
|
94
|
-
|
|
94
|
+
messages: "https://messages.topolo.app",
|
|
95
95
|
nexus: "https://nexus-api.topolo.app",
|
|
96
96
|
notify: "https://notify.topolo.app",
|
|
97
|
+
observability_watch: "https://observability-watch.topolo.app",
|
|
97
98
|
one: "https://one.topolo.app",
|
|
98
99
|
p2p: "https://p2p.topolo.app",
|
|
99
100
|
pay: "https://pay.topolo.app",
|
|
@@ -107,6 +108,7 @@ var SERVICES = {
|
|
|
107
108
|
socialize: "https://socialize-api.topolo.app",
|
|
108
109
|
spaces: "https://spaces.topolo.app",
|
|
109
110
|
status: "https://status.topolo.app",
|
|
111
|
+
success: "https://success.topolo.app",
|
|
110
112
|
support: "https://support.topolo.app",
|
|
111
113
|
survey: "https://survey.topolo.app",
|
|
112
114
|
voice: "https://voice.topolo.app",
|
|
@@ -123,13 +125,21 @@ var APPLICATIONS = {
|
|
|
123
125
|
"deployTargets": [
|
|
124
126
|
{
|
|
125
127
|
"name": "topolo-admin",
|
|
126
|
-
"kind": "
|
|
127
|
-
"apiUrl":
|
|
128
|
+
"kind": "worker",
|
|
129
|
+
"apiUrl": "https://admin.topolo.app",
|
|
128
130
|
"domains": [
|
|
129
131
|
"admin.topolo.app"
|
|
130
132
|
],
|
|
131
133
|
"routes": [],
|
|
132
134
|
"serviceId": null
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"name": "topolo-admin-api",
|
|
138
|
+
"kind": "worker",
|
|
139
|
+
"apiUrl": null,
|
|
140
|
+
"domains": [],
|
|
141
|
+
"routes": [],
|
|
142
|
+
"serviceId": null
|
|
133
143
|
}
|
|
134
144
|
]
|
|
135
145
|
},
|
|
@@ -146,7 +156,7 @@ var APPLICATIONS = {
|
|
|
146
156
|
{
|
|
147
157
|
"name": "topolo-agent",
|
|
148
158
|
"kind": "worker",
|
|
149
|
-
"apiUrl":
|
|
159
|
+
"apiUrl": "https://agent.topolo.app",
|
|
150
160
|
"domains": [
|
|
151
161
|
"agent.topolo.app"
|
|
152
162
|
],
|
|
@@ -154,7 +164,7 @@ var APPLICATIONS = {
|
|
|
154
164
|
"serviceId": null
|
|
155
165
|
},
|
|
156
166
|
{
|
|
157
|
-
"name": "topolo-agent-
|
|
167
|
+
"name": "topolo-agent-api",
|
|
158
168
|
"kind": "worker",
|
|
159
169
|
"apiUrl": "https://agent-api.topolo.app",
|
|
160
170
|
"domains": [
|
|
@@ -194,16 +204,20 @@ var APPLICATIONS = {
|
|
|
194
204
|
"appDir": "TopoloBackup",
|
|
195
205
|
"name": "topolo-backup",
|
|
196
206
|
"packageName": "topolo-backup",
|
|
197
|
-
"productionUrl":
|
|
198
|
-
"services": [
|
|
207
|
+
"productionUrl": "https://backup.topolo.app",
|
|
208
|
+
"services": [
|
|
209
|
+
"backup"
|
|
210
|
+
],
|
|
199
211
|
"deployTargets": [
|
|
200
212
|
{
|
|
201
213
|
"name": "topolo-backup",
|
|
202
214
|
"kind": "worker",
|
|
203
|
-
"apiUrl":
|
|
204
|
-
"domains": [
|
|
215
|
+
"apiUrl": "https://backup.topolo.app",
|
|
216
|
+
"domains": [
|
|
217
|
+
"backup.topolo.app"
|
|
218
|
+
],
|
|
205
219
|
"routes": [],
|
|
206
|
-
"serviceId":
|
|
220
|
+
"serviceId": "backup"
|
|
207
221
|
}
|
|
208
222
|
]
|
|
209
223
|
},
|
|
@@ -224,10 +238,16 @@ var APPLICATIONS = {
|
|
|
224
238
|
"domains": [
|
|
225
239
|
"books.topolo.app"
|
|
226
240
|
],
|
|
227
|
-
"routes": [
|
|
228
|
-
"books.topolo.app"
|
|
229
|
-
],
|
|
241
|
+
"routes": [],
|
|
230
242
|
"serviceId": "books"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"name": "topolo-books-api",
|
|
246
|
+
"kind": "worker",
|
|
247
|
+
"apiUrl": null,
|
|
248
|
+
"domains": [],
|
|
249
|
+
"routes": [],
|
|
250
|
+
"serviceId": null
|
|
231
251
|
}
|
|
232
252
|
]
|
|
233
253
|
},
|
|
@@ -248,9 +268,7 @@ var APPLICATIONS = {
|
|
|
248
268
|
"domains": [
|
|
249
269
|
"bugfix.topolo.app"
|
|
250
270
|
],
|
|
251
|
-
"routes": [
|
|
252
|
-
"bugfix.topolo.app"
|
|
253
|
-
],
|
|
271
|
+
"routes": [],
|
|
254
272
|
"serviceId": "bugfix"
|
|
255
273
|
}
|
|
256
274
|
]
|
|
@@ -267,8 +285,8 @@ var APPLICATIONS = {
|
|
|
267
285
|
"deployTargets": [
|
|
268
286
|
{
|
|
269
287
|
"name": "topolo-bytes",
|
|
270
|
-
"kind": "
|
|
271
|
-
"apiUrl":
|
|
288
|
+
"kind": "worker",
|
|
289
|
+
"apiUrl": "https://bytes.topolo.app",
|
|
272
290
|
"domains": [
|
|
273
291
|
"bytes.topolo.app"
|
|
274
292
|
],
|
|
@@ -304,10 +322,16 @@ var APPLICATIONS = {
|
|
|
304
322
|
"domains": [
|
|
305
323
|
"calendar.topolo.app"
|
|
306
324
|
],
|
|
307
|
-
"routes": [
|
|
308
|
-
"calendar.topolo.app"
|
|
309
|
-
],
|
|
325
|
+
"routes": [],
|
|
310
326
|
"serviceId": "calendar"
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"name": "topolo-calendar-api",
|
|
330
|
+
"kind": "worker",
|
|
331
|
+
"apiUrl": null,
|
|
332
|
+
"domains": [],
|
|
333
|
+
"routes": [],
|
|
334
|
+
"serviceId": null
|
|
311
335
|
}
|
|
312
336
|
]
|
|
313
337
|
},
|
|
@@ -350,22 +374,19 @@ var APPLICATIONS = {
|
|
|
350
374
|
"domains": [
|
|
351
375
|
"chat.topolo.app"
|
|
352
376
|
],
|
|
353
|
-
"routes": [
|
|
354
|
-
"chat.topolo.app"
|
|
355
|
-
],
|
|
377
|
+
"routes": [],
|
|
356
378
|
"serviceId": "chat"
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"name": "topolo-chat-api",
|
|
382
|
+
"kind": "worker",
|
|
383
|
+
"apiUrl": null,
|
|
384
|
+
"domains": [],
|
|
385
|
+
"routes": [],
|
|
386
|
+
"serviceId": null
|
|
357
387
|
}
|
|
358
388
|
]
|
|
359
389
|
},
|
|
360
|
-
"cloudcontrol": {
|
|
361
|
-
"id": "cloudcontrol",
|
|
362
|
-
"appDir": "TopoloCloudControl",
|
|
363
|
-
"name": "cloudcontrol",
|
|
364
|
-
"packageName": "topolo-cloudcontrol",
|
|
365
|
-
"productionUrl": null,
|
|
366
|
-
"services": [],
|
|
367
|
-
"deployTargets": []
|
|
368
|
-
},
|
|
369
390
|
"commerce": {
|
|
370
391
|
"id": "commerce",
|
|
371
392
|
"appDir": "TopoloCommerce",
|
|
@@ -388,8 +409,8 @@ var APPLICATIONS = {
|
|
|
388
409
|
},
|
|
389
410
|
{
|
|
390
411
|
"name": "topolo-commerce-guest-web",
|
|
391
|
-
"kind": "
|
|
392
|
-
"apiUrl":
|
|
412
|
+
"kind": "worker",
|
|
413
|
+
"apiUrl": "https://guest.topolo.app",
|
|
393
414
|
"domains": [
|
|
394
415
|
"guest.topolo.app"
|
|
395
416
|
],
|
|
@@ -398,8 +419,8 @@ var APPLICATIONS = {
|
|
|
398
419
|
},
|
|
399
420
|
{
|
|
400
421
|
"name": "topolo-commerce-ops-web",
|
|
401
|
-
"kind": "
|
|
402
|
-
"apiUrl":
|
|
422
|
+
"kind": "worker",
|
|
423
|
+
"apiUrl": "https://commerce.topolo.app",
|
|
403
424
|
"domains": [
|
|
404
425
|
"commerce.topolo.app"
|
|
405
426
|
],
|
|
@@ -408,8 +429,8 @@ var APPLICATIONS = {
|
|
|
408
429
|
},
|
|
409
430
|
{
|
|
410
431
|
"name": "topolo-commerce-station-web",
|
|
411
|
-
"kind": "
|
|
412
|
-
"apiUrl":
|
|
432
|
+
"kind": "worker",
|
|
433
|
+
"apiUrl": "https://station-commerce.topolo.app",
|
|
413
434
|
"domains": [
|
|
414
435
|
"station-commerce.topolo.app"
|
|
415
436
|
],
|
|
@@ -435,10 +456,16 @@ var APPLICATIONS = {
|
|
|
435
456
|
"domains": [
|
|
436
457
|
"compose.topolo.app"
|
|
437
458
|
],
|
|
438
|
-
"routes": [
|
|
439
|
-
"compose.topolo.app"
|
|
440
|
-
],
|
|
459
|
+
"routes": [],
|
|
441
460
|
"serviceId": "compose"
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
"name": "topolo-compose-api",
|
|
464
|
+
"kind": "worker",
|
|
465
|
+
"apiUrl": null,
|
|
466
|
+
"domains": [],
|
|
467
|
+
"routes": [],
|
|
468
|
+
"serviceId": null
|
|
442
469
|
}
|
|
443
470
|
]
|
|
444
471
|
},
|
|
@@ -459,10 +486,16 @@ var APPLICATIONS = {
|
|
|
459
486
|
"domains": [
|
|
460
487
|
"consent.topolo.app"
|
|
461
488
|
],
|
|
462
|
-
"routes": [
|
|
463
|
-
"consent.topolo.app"
|
|
464
|
-
],
|
|
489
|
+
"routes": [],
|
|
465
490
|
"serviceId": "consent"
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
"name": "topolo-consent-api",
|
|
494
|
+
"kind": "worker",
|
|
495
|
+
"apiUrl": null,
|
|
496
|
+
"domains": [],
|
|
497
|
+
"routes": [],
|
|
498
|
+
"serviceId": null
|
|
466
499
|
}
|
|
467
500
|
]
|
|
468
501
|
},
|
|
@@ -489,7 +522,7 @@ var APPLICATIONS = {
|
|
|
489
522
|
{
|
|
490
523
|
"name": "topolo-crm-app",
|
|
491
524
|
"kind": "worker",
|
|
492
|
-
"apiUrl":
|
|
525
|
+
"apiUrl": "https://crm.topolo.app",
|
|
493
526
|
"domains": [
|
|
494
527
|
"crm.topolo.app"
|
|
495
528
|
],
|
|
@@ -498,30 +531,6 @@ var APPLICATIONS = {
|
|
|
498
531
|
}
|
|
499
532
|
]
|
|
500
533
|
},
|
|
501
|
-
"customer_success": {
|
|
502
|
-
"id": "customer_success",
|
|
503
|
-
"appDir": "TopoloSuccess",
|
|
504
|
-
"name": "topolo-success",
|
|
505
|
-
"packageName": "topolo-success",
|
|
506
|
-
"productionUrl": "https://success.topolo.app",
|
|
507
|
-
"services": [
|
|
508
|
-
"customer_success"
|
|
509
|
-
],
|
|
510
|
-
"deployTargets": [
|
|
511
|
-
{
|
|
512
|
-
"name": "topolo-success",
|
|
513
|
-
"kind": "worker",
|
|
514
|
-
"apiUrl": "https://success.topolo.app",
|
|
515
|
-
"domains": [
|
|
516
|
-
"success.topolo.app"
|
|
517
|
-
],
|
|
518
|
-
"routes": [
|
|
519
|
-
"success.topolo.app"
|
|
520
|
-
],
|
|
521
|
-
"serviceId": "customer_success"
|
|
522
|
-
}
|
|
523
|
-
]
|
|
524
|
-
},
|
|
525
534
|
"developers": {
|
|
526
535
|
"id": "developers",
|
|
527
536
|
"appDir": "TopoloDevelopers",
|
|
@@ -549,6 +558,14 @@ var APPLICATIONS = {
|
|
|
549
558
|
],
|
|
550
559
|
"routes": [],
|
|
551
560
|
"serviceId": "developers"
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
"name": "topolo-developers-api",
|
|
564
|
+
"kind": "worker",
|
|
565
|
+
"apiUrl": null,
|
|
566
|
+
"domains": [],
|
|
567
|
+
"routes": [],
|
|
568
|
+
"serviceId": null
|
|
552
569
|
}
|
|
553
570
|
]
|
|
554
571
|
},
|
|
@@ -569,10 +586,16 @@ var APPLICATIONS = {
|
|
|
569
586
|
"domains": [
|
|
570
587
|
"director.topolo.app"
|
|
571
588
|
],
|
|
572
|
-
"routes": [
|
|
573
|
-
"director.topolo.app"
|
|
574
|
-
],
|
|
589
|
+
"routes": [],
|
|
575
590
|
"serviceId": "director"
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
"name": "topolo-director-api",
|
|
594
|
+
"kind": "worker",
|
|
595
|
+
"apiUrl": null,
|
|
596
|
+
"domains": [],
|
|
597
|
+
"routes": [],
|
|
598
|
+
"serviceId": null
|
|
576
599
|
}
|
|
577
600
|
]
|
|
578
601
|
},
|
|
@@ -586,13 +609,21 @@ var APPLICATIONS = {
|
|
|
586
609
|
"deployTargets": [
|
|
587
610
|
{
|
|
588
611
|
"name": "topolo-docs",
|
|
589
|
-
"kind": "
|
|
590
|
-
"apiUrl":
|
|
612
|
+
"kind": "worker",
|
|
613
|
+
"apiUrl": "https://docs.topolo.app",
|
|
591
614
|
"domains": [
|
|
592
615
|
"docs.topolo.app"
|
|
593
616
|
],
|
|
594
617
|
"routes": [],
|
|
595
618
|
"serviceId": null
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
"name": "topolo-docs-api",
|
|
622
|
+
"kind": "worker",
|
|
623
|
+
"apiUrl": null,
|
|
624
|
+
"domains": [],
|
|
625
|
+
"routes": [],
|
|
626
|
+
"serviceId": null
|
|
596
627
|
}
|
|
597
628
|
]
|
|
598
629
|
},
|
|
@@ -601,7 +632,7 @@ var APPLICATIONS = {
|
|
|
601
632
|
"appDir": "TopoloFeed",
|
|
602
633
|
"name": "TopoloFeed",
|
|
603
634
|
"packageName": "topolo-feed",
|
|
604
|
-
"productionUrl": "https://feed
|
|
635
|
+
"productionUrl": "https://feed.topolo.app",
|
|
605
636
|
"services": [
|
|
606
637
|
"feed"
|
|
607
638
|
],
|
|
@@ -613,9 +644,7 @@ var APPLICATIONS = {
|
|
|
613
644
|
"domains": [
|
|
614
645
|
"feed-analytics-api.topolo.app"
|
|
615
646
|
],
|
|
616
|
-
"routes": [
|
|
617
|
-
"feed-analytics-api.topolo.app"
|
|
618
|
-
],
|
|
647
|
+
"routes": [],
|
|
619
648
|
"serviceId": null
|
|
620
649
|
},
|
|
621
650
|
{
|
|
@@ -625,10 +654,18 @@ var APPLICATIONS = {
|
|
|
625
654
|
"domains": [
|
|
626
655
|
"feed-api.topolo.app"
|
|
627
656
|
],
|
|
628
|
-
"routes": [
|
|
629
|
-
"feed-api.topolo.app"
|
|
630
|
-
],
|
|
657
|
+
"routes": [],
|
|
631
658
|
"serviceId": "feed"
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
"name": "topolo-feed-web",
|
|
662
|
+
"kind": "worker",
|
|
663
|
+
"apiUrl": "https://feed.topolo.app",
|
|
664
|
+
"domains": [
|
|
665
|
+
"feed.topolo.app"
|
|
666
|
+
],
|
|
667
|
+
"routes": [],
|
|
668
|
+
"serviceId": null
|
|
632
669
|
}
|
|
633
670
|
]
|
|
634
671
|
},
|
|
@@ -649,17 +686,23 @@ var APPLICATIONS = {
|
|
|
649
686
|
"domains": [
|
|
650
687
|
"flow.topolo.app"
|
|
651
688
|
],
|
|
652
|
-
"routes": [
|
|
653
|
-
"flow.topolo.app"
|
|
654
|
-
],
|
|
689
|
+
"routes": [],
|
|
655
690
|
"serviceId": "flow"
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
"name": "topolo-flow-api",
|
|
694
|
+
"kind": "worker",
|
|
695
|
+
"apiUrl": null,
|
|
696
|
+
"domains": [],
|
|
697
|
+
"routes": [],
|
|
698
|
+
"serviceId": null
|
|
656
699
|
}
|
|
657
700
|
]
|
|
658
701
|
},
|
|
659
702
|
"forecast": {
|
|
660
703
|
"id": "forecast",
|
|
661
704
|
"appDir": "TopoloForecast",
|
|
662
|
-
"name": "forecast
|
|
705
|
+
"name": "topolo-forecast",
|
|
663
706
|
"packageName": "topolo-forecast",
|
|
664
707
|
"productionUrl": "https://forecast.topolo.app",
|
|
665
708
|
"services": [
|
|
@@ -669,7 +712,7 @@ var APPLICATIONS = {
|
|
|
669
712
|
{
|
|
670
713
|
"name": "topolo-forecast",
|
|
671
714
|
"kind": "worker",
|
|
672
|
-
"apiUrl":
|
|
715
|
+
"apiUrl": "https://forecast.topolo.app",
|
|
673
716
|
"domains": [
|
|
674
717
|
"forecast.topolo.app"
|
|
675
718
|
],
|
|
@@ -705,10 +748,16 @@ var APPLICATIONS = {
|
|
|
705
748
|
"domains": [
|
|
706
749
|
"forms.topolo.app"
|
|
707
750
|
],
|
|
708
|
-
"routes": [
|
|
709
|
-
"forms.topolo.app"
|
|
710
|
-
],
|
|
751
|
+
"routes": [],
|
|
711
752
|
"serviceId": "forms"
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
"name": "topolo-forms-api",
|
|
756
|
+
"kind": "worker",
|
|
757
|
+
"apiUrl": null,
|
|
758
|
+
"domains": [],
|
|
759
|
+
"routes": [],
|
|
760
|
+
"serviceId": null
|
|
712
761
|
}
|
|
713
762
|
]
|
|
714
763
|
},
|
|
@@ -729,10 +778,16 @@ var APPLICATIONS = {
|
|
|
729
778
|
"domains": [
|
|
730
779
|
"home.topolo.app"
|
|
731
780
|
],
|
|
732
|
-
"routes": [
|
|
733
|
-
"home.topolo.app"
|
|
734
|
-
],
|
|
781
|
+
"routes": [],
|
|
735
782
|
"serviceId": "home"
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
"name": "topolo-home-api",
|
|
786
|
+
"kind": "worker",
|
|
787
|
+
"apiUrl": null,
|
|
788
|
+
"domains": [],
|
|
789
|
+
"routes": [],
|
|
790
|
+
"serviceId": null
|
|
736
791
|
}
|
|
737
792
|
]
|
|
738
793
|
},
|
|
@@ -753,10 +808,16 @@ var APPLICATIONS = {
|
|
|
753
808
|
"domains": [
|
|
754
809
|
"insights.topolo.app"
|
|
755
810
|
],
|
|
756
|
-
"routes": [
|
|
757
|
-
"insights.topolo.app"
|
|
758
|
-
],
|
|
811
|
+
"routes": [],
|
|
759
812
|
"serviceId": "insights"
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
"name": "topolo-insights-api",
|
|
816
|
+
"kind": "worker",
|
|
817
|
+
"apiUrl": null,
|
|
818
|
+
"domains": [],
|
|
819
|
+
"routes": [],
|
|
820
|
+
"serviceId": null
|
|
760
821
|
}
|
|
761
822
|
]
|
|
762
823
|
},
|
|
@@ -777,10 +838,16 @@ var APPLICATIONS = {
|
|
|
777
838
|
"domains": [
|
|
778
839
|
"inventory.topolo.app"
|
|
779
840
|
],
|
|
780
|
-
"routes": [
|
|
781
|
-
"inventory.topolo.app"
|
|
782
|
-
],
|
|
841
|
+
"routes": [],
|
|
783
842
|
"serviceId": "inventory"
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
"name": "topolo-inventory-api",
|
|
846
|
+
"kind": "worker",
|
|
847
|
+
"apiUrl": null,
|
|
848
|
+
"domains": [],
|
|
849
|
+
"routes": [],
|
|
850
|
+
"serviceId": null
|
|
784
851
|
}
|
|
785
852
|
]
|
|
786
853
|
},
|
|
@@ -797,7 +864,7 @@ var APPLICATIONS = {
|
|
|
797
864
|
{
|
|
798
865
|
"name": "topolo-learn",
|
|
799
866
|
"kind": "worker",
|
|
800
|
-
"apiUrl":
|
|
867
|
+
"apiUrl": "https://learn.topolo.app",
|
|
801
868
|
"domains": [
|
|
802
869
|
"learn.topolo.app"
|
|
803
870
|
],
|
|
@@ -857,10 +924,16 @@ var APPLICATIONS = {
|
|
|
857
924
|
"domains": [
|
|
858
925
|
"mail.topolo.app"
|
|
859
926
|
],
|
|
860
|
-
"routes": [
|
|
861
|
-
"mail.topolo.app"
|
|
862
|
-
],
|
|
927
|
+
"routes": [],
|
|
863
928
|
"serviceId": "mail"
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
"name": "topolo-mail-api",
|
|
932
|
+
"kind": "worker",
|
|
933
|
+
"apiUrl": null,
|
|
934
|
+
"domains": [],
|
|
935
|
+
"routes": [],
|
|
936
|
+
"serviceId": null
|
|
864
937
|
}
|
|
865
938
|
]
|
|
866
939
|
},
|
|
@@ -881,47 +954,51 @@ var APPLICATIONS = {
|
|
|
881
954
|
"domains": [
|
|
882
955
|
"mdm-api.topolo.app"
|
|
883
956
|
],
|
|
884
|
-
"routes": [
|
|
885
|
-
"mdm-api.topolo.app"
|
|
886
|
-
],
|
|
957
|
+
"routes": [],
|
|
887
958
|
"serviceId": "mdm"
|
|
888
959
|
},
|
|
889
960
|
{
|
|
890
961
|
"name": "topolo-mdm-console",
|
|
891
962
|
"kind": "worker",
|
|
892
|
-
"apiUrl":
|
|
963
|
+
"apiUrl": "https://mdm.topolo.app",
|
|
893
964
|
"domains": [
|
|
894
965
|
"mdm.topolo.app"
|
|
895
966
|
],
|
|
896
|
-
"routes": [
|
|
897
|
-
"mdm.topolo.app"
|
|
898
|
-
],
|
|
967
|
+
"routes": [],
|
|
899
968
|
"serviceId": null
|
|
900
969
|
}
|
|
901
970
|
]
|
|
902
971
|
},
|
|
903
|
-
"
|
|
904
|
-
"id": "
|
|
905
|
-
"appDir": "
|
|
906
|
-
"name": "topolo-
|
|
907
|
-
"packageName": "topolo-
|
|
972
|
+
"messages": {
|
|
973
|
+
"id": "messages",
|
|
974
|
+
"appDir": "TopoloMessages",
|
|
975
|
+
"name": "topolo-messages",
|
|
976
|
+
"packageName": "topolo-messages",
|
|
908
977
|
"productionUrl": "https://messages.topolo.app",
|
|
909
978
|
"services": [
|
|
910
|
-
"
|
|
979
|
+
"messages"
|
|
911
980
|
],
|
|
912
981
|
"deployTargets": [
|
|
913
982
|
{
|
|
914
|
-
"name": "topolo-
|
|
983
|
+
"name": "topolo-messages",
|
|
915
984
|
"kind": "worker",
|
|
916
985
|
"apiUrl": "https://messages.topolo.app",
|
|
917
986
|
"domains": [
|
|
918
987
|
"messages.topolo.app"
|
|
919
988
|
],
|
|
920
989
|
"routes": [],
|
|
921
|
-
"serviceId": "
|
|
990
|
+
"serviceId": "messages"
|
|
922
991
|
},
|
|
923
992
|
{
|
|
924
|
-
"name": "topolo-
|
|
993
|
+
"name": "topolo-messages-api",
|
|
994
|
+
"kind": "worker",
|
|
995
|
+
"apiUrl": null,
|
|
996
|
+
"domains": [],
|
|
997
|
+
"routes": [],
|
|
998
|
+
"serviceId": null
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
"name": "topolo-messages-dispatch",
|
|
925
1002
|
"kind": "worker",
|
|
926
1003
|
"apiUrl": null,
|
|
927
1004
|
"domains": [],
|
|
@@ -991,9 +1068,22 @@ var APPLICATIONS = {
|
|
|
991
1068
|
"appDir": "TopoloObservabilityWatch",
|
|
992
1069
|
"name": "topolo-observability-watch",
|
|
993
1070
|
"packageName": "topolo-observability-watch",
|
|
994
|
-
"productionUrl":
|
|
995
|
-
"services": [
|
|
996
|
-
|
|
1071
|
+
"productionUrl": "https://observability-watch.topolo.app",
|
|
1072
|
+
"services": [
|
|
1073
|
+
"observability_watch"
|
|
1074
|
+
],
|
|
1075
|
+
"deployTargets": [
|
|
1076
|
+
{
|
|
1077
|
+
"name": "topolo-observability-watch",
|
|
1078
|
+
"kind": "worker",
|
|
1079
|
+
"apiUrl": "https://observability-watch.topolo.app",
|
|
1080
|
+
"domains": [
|
|
1081
|
+
"observability-watch.topolo.app"
|
|
1082
|
+
],
|
|
1083
|
+
"routes": [],
|
|
1084
|
+
"serviceId": "observability_watch"
|
|
1085
|
+
}
|
|
1086
|
+
]
|
|
997
1087
|
},
|
|
998
1088
|
"one": {
|
|
999
1089
|
"id": "one",
|
|
@@ -1007,40 +1097,37 @@ var APPLICATIONS = {
|
|
|
1007
1097
|
"deployTargets": [
|
|
1008
1098
|
{
|
|
1009
1099
|
"name": "topolo-one",
|
|
1010
|
-
"kind": "pages",
|
|
1011
|
-
"apiUrl": null,
|
|
1012
|
-
"domains": [
|
|
1013
|
-
"one.topolo.app"
|
|
1014
|
-
],
|
|
1015
|
-
"routes": [],
|
|
1016
|
-
"serviceId": null
|
|
1017
|
-
},
|
|
1018
|
-
{
|
|
1019
|
-
"name": "topolo-one-website",
|
|
1020
1100
|
"kind": "worker",
|
|
1021
1101
|
"apiUrl": null,
|
|
1022
|
-
"domains": [
|
|
1023
|
-
"topolo.io",
|
|
1024
|
-
"www.topolo.io"
|
|
1025
|
-
],
|
|
1102
|
+
"domains": [],
|
|
1026
1103
|
"routes": [],
|
|
1027
1104
|
"serviceId": null
|
|
1028
1105
|
},
|
|
1029
1106
|
{
|
|
1030
|
-
"name": "topolo-one-
|
|
1107
|
+
"name": "topolo-one-api",
|
|
1031
1108
|
"kind": "worker",
|
|
1032
1109
|
"apiUrl": "https://one.topolo.app",
|
|
1033
1110
|
"domains": [
|
|
1034
|
-
"one.topolo.app"
|
|
1035
|
-
"topolo.app",
|
|
1036
|
-
"www.topolo.app"
|
|
1111
|
+
"one.topolo.app"
|
|
1037
1112
|
],
|
|
1038
1113
|
"routes": [
|
|
1039
1114
|
"one.topolo.app/*",
|
|
1040
|
-
"topolo.app",
|
|
1041
1115
|
"www.topolo.app/*"
|
|
1042
1116
|
],
|
|
1043
1117
|
"serviceId": "one"
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
"name": "topolo-one-website",
|
|
1121
|
+
"kind": "worker",
|
|
1122
|
+
"apiUrl": "https://topolo.io",
|
|
1123
|
+
"domains": [
|
|
1124
|
+
"topolo.io"
|
|
1125
|
+
],
|
|
1126
|
+
"routes": [
|
|
1127
|
+
"topolo.io",
|
|
1128
|
+
"www.topolo.io"
|
|
1129
|
+
],
|
|
1130
|
+
"serviceId": null
|
|
1044
1131
|
}
|
|
1045
1132
|
]
|
|
1046
1133
|
},
|
|
@@ -1061,10 +1148,16 @@ var APPLICATIONS = {
|
|
|
1061
1148
|
"domains": [
|
|
1062
1149
|
"p2p.topolo.app"
|
|
1063
1150
|
],
|
|
1064
|
-
"routes": [
|
|
1065
|
-
"p2p.topolo.app"
|
|
1066
|
-
],
|
|
1151
|
+
"routes": [],
|
|
1067
1152
|
"serviceId": "p2p"
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
"name": "topolo-p2p-api",
|
|
1156
|
+
"kind": "worker",
|
|
1157
|
+
"apiUrl": null,
|
|
1158
|
+
"domains": [],
|
|
1159
|
+
"routes": [],
|
|
1160
|
+
"serviceId": null
|
|
1068
1161
|
}
|
|
1069
1162
|
]
|
|
1070
1163
|
},
|
|
@@ -1087,6 +1180,14 @@ var APPLICATIONS = {
|
|
|
1087
1180
|
],
|
|
1088
1181
|
"routes": [],
|
|
1089
1182
|
"serviceId": "pay"
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
"name": "topolo-pay-api",
|
|
1186
|
+
"kind": "worker",
|
|
1187
|
+
"apiUrl": null,
|
|
1188
|
+
"domains": [],
|
|
1189
|
+
"routes": [],
|
|
1190
|
+
"serviceId": null
|
|
1090
1191
|
}
|
|
1091
1192
|
]
|
|
1092
1193
|
},
|
|
@@ -1107,10 +1208,16 @@ var APPLICATIONS = {
|
|
|
1107
1208
|
"domains": [
|
|
1108
1209
|
"people.topolo.app"
|
|
1109
1210
|
],
|
|
1110
|
-
"routes": [
|
|
1111
|
-
"people.topolo.app"
|
|
1112
|
-
],
|
|
1211
|
+
"routes": [],
|
|
1113
1212
|
"serviceId": "people"
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
"name": "topolo-people-api",
|
|
1216
|
+
"kind": "worker",
|
|
1217
|
+
"apiUrl": null,
|
|
1218
|
+
"domains": [],
|
|
1219
|
+
"routes": [],
|
|
1220
|
+
"serviceId": null
|
|
1114
1221
|
}
|
|
1115
1222
|
]
|
|
1116
1223
|
},
|
|
@@ -1131,10 +1238,16 @@ var APPLICATIONS = {
|
|
|
1131
1238
|
"domains": [
|
|
1132
1239
|
"pulse.topolo.app"
|
|
1133
1240
|
],
|
|
1134
|
-
"routes": [
|
|
1135
|
-
"pulse.topolo.app"
|
|
1136
|
-
],
|
|
1241
|
+
"routes": [],
|
|
1137
1242
|
"serviceId": "pulse"
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
"name": "topolo-pulse-api",
|
|
1246
|
+
"kind": "worker",
|
|
1247
|
+
"apiUrl": null,
|
|
1248
|
+
"domains": [],
|
|
1249
|
+
"routes": [],
|
|
1250
|
+
"serviceId": null
|
|
1138
1251
|
}
|
|
1139
1252
|
]
|
|
1140
1253
|
},
|
|
@@ -1156,7 +1269,9 @@ var APPLICATIONS = {
|
|
|
1156
1269
|
"domains": [
|
|
1157
1270
|
"api.ol0.me"
|
|
1158
1271
|
],
|
|
1159
|
-
"routes": [
|
|
1272
|
+
"routes": [
|
|
1273
|
+
"api.ol0.me/*"
|
|
1274
|
+
],
|
|
1160
1275
|
"serviceId": "quro.api"
|
|
1161
1276
|
},
|
|
1162
1277
|
{
|
|
@@ -1174,7 +1289,7 @@ var APPLICATIONS = {
|
|
|
1174
1289
|
{
|
|
1175
1290
|
"name": "quro-ui",
|
|
1176
1291
|
"kind": "worker",
|
|
1177
|
-
"apiUrl":
|
|
1292
|
+
"apiUrl": "https://quro.topolo.app",
|
|
1178
1293
|
"domains": [
|
|
1179
1294
|
"quro.topolo.app"
|
|
1180
1295
|
],
|
|
@@ -1196,7 +1311,7 @@ var APPLICATIONS = {
|
|
|
1196
1311
|
{
|
|
1197
1312
|
"name": "topolo-roadmapper",
|
|
1198
1313
|
"kind": "worker",
|
|
1199
|
-
"apiUrl":
|
|
1314
|
+
"apiUrl": "https://roadmapper.topolo.app",
|
|
1200
1315
|
"domains": [
|
|
1201
1316
|
"roadmapper.topolo.app"
|
|
1202
1317
|
],
|
|
@@ -1232,10 +1347,16 @@ var APPLICATIONS = {
|
|
|
1232
1347
|
"domains": [
|
|
1233
1348
|
"sign.topolo.app"
|
|
1234
1349
|
],
|
|
1235
|
-
"routes": [
|
|
1236
|
-
"sign.topolo.app"
|
|
1237
|
-
],
|
|
1350
|
+
"routes": [],
|
|
1238
1351
|
"serviceId": "sign"
|
|
1352
|
+
},
|
|
1353
|
+
{
|
|
1354
|
+
"name": "topolo-sign-api",
|
|
1355
|
+
"kind": "worker",
|
|
1356
|
+
"apiUrl": null,
|
|
1357
|
+
"domains": [],
|
|
1358
|
+
"routes": [],
|
|
1359
|
+
"serviceId": null
|
|
1239
1360
|
}
|
|
1240
1361
|
]
|
|
1241
1362
|
},
|
|
@@ -1252,7 +1373,7 @@ var APPLICATIONS = {
|
|
|
1252
1373
|
{
|
|
1253
1374
|
"name": "topolo-social-studio",
|
|
1254
1375
|
"kind": "worker",
|
|
1255
|
-
"apiUrl":
|
|
1376
|
+
"apiUrl": "https://studio.topolo.app",
|
|
1256
1377
|
"domains": [
|
|
1257
1378
|
"studio.topolo.app"
|
|
1258
1379
|
],
|
|
@@ -1291,8 +1412,8 @@ var APPLICATIONS = {
|
|
|
1291
1412
|
"deployTargets": [
|
|
1292
1413
|
{
|
|
1293
1414
|
"name": "topolo-socialize",
|
|
1294
|
-
"kind": "
|
|
1295
|
-
"apiUrl":
|
|
1415
|
+
"kind": "worker",
|
|
1416
|
+
"apiUrl": "https://socialize.topolo.app",
|
|
1296
1417
|
"domains": [
|
|
1297
1418
|
"socialize.topolo.app"
|
|
1298
1419
|
],
|
|
@@ -1336,10 +1457,16 @@ var APPLICATIONS = {
|
|
|
1336
1457
|
"domains": [
|
|
1337
1458
|
"spaces.topolo.app"
|
|
1338
1459
|
],
|
|
1339
|
-
"routes": [
|
|
1340
|
-
"spaces.topolo.app"
|
|
1341
|
-
],
|
|
1460
|
+
"routes": [],
|
|
1342
1461
|
"serviceId": "spaces"
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
"name": "topolo-spaces-api",
|
|
1465
|
+
"kind": "worker",
|
|
1466
|
+
"apiUrl": null,
|
|
1467
|
+
"domains": [],
|
|
1468
|
+
"routes": [],
|
|
1469
|
+
"serviceId": null
|
|
1343
1470
|
}
|
|
1344
1471
|
]
|
|
1345
1472
|
},
|
|
@@ -1360,10 +1487,40 @@ var APPLICATIONS = {
|
|
|
1360
1487
|
"domains": [
|
|
1361
1488
|
"status.topolo.app"
|
|
1362
1489
|
],
|
|
1490
|
+
"routes": [],
|
|
1491
|
+
"serviceId": "status"
|
|
1492
|
+
}
|
|
1493
|
+
]
|
|
1494
|
+
},
|
|
1495
|
+
"success": {
|
|
1496
|
+
"id": "success",
|
|
1497
|
+
"appDir": "TopoloSuccess",
|
|
1498
|
+
"name": "topolo-success",
|
|
1499
|
+
"packageName": "topolo-success",
|
|
1500
|
+
"productionUrl": "https://success.topolo.app",
|
|
1501
|
+
"services": [
|
|
1502
|
+
"success"
|
|
1503
|
+
],
|
|
1504
|
+
"deployTargets": [
|
|
1505
|
+
{
|
|
1506
|
+
"name": "topolo-success",
|
|
1507
|
+
"kind": "worker",
|
|
1508
|
+
"apiUrl": "https://success.topolo.app",
|
|
1509
|
+
"domains": [
|
|
1510
|
+
"success.topolo.app"
|
|
1511
|
+
],
|
|
1363
1512
|
"routes": [
|
|
1364
|
-
"
|
|
1513
|
+
"success.topolo.app"
|
|
1365
1514
|
],
|
|
1366
|
-
"serviceId": "
|
|
1515
|
+
"serviceId": "success"
|
|
1516
|
+
},
|
|
1517
|
+
{
|
|
1518
|
+
"name": "topolo-success-api",
|
|
1519
|
+
"kind": "worker",
|
|
1520
|
+
"apiUrl": null,
|
|
1521
|
+
"domains": [],
|
|
1522
|
+
"routes": [],
|
|
1523
|
+
"serviceId": null
|
|
1367
1524
|
}
|
|
1368
1525
|
]
|
|
1369
1526
|
},
|
|
@@ -1388,6 +1545,14 @@ var APPLICATIONS = {
|
|
|
1388
1545
|
"support.topolo.app/*"
|
|
1389
1546
|
],
|
|
1390
1547
|
"serviceId": "support"
|
|
1548
|
+
},
|
|
1549
|
+
{
|
|
1550
|
+
"name": "topolo-support-api",
|
|
1551
|
+
"kind": "worker",
|
|
1552
|
+
"apiUrl": null,
|
|
1553
|
+
"domains": [],
|
|
1554
|
+
"routes": [],
|
|
1555
|
+
"serviceId": null
|
|
1391
1556
|
}
|
|
1392
1557
|
]
|
|
1393
1558
|
},
|
|
@@ -1408,10 +1573,16 @@ var APPLICATIONS = {
|
|
|
1408
1573
|
"domains": [
|
|
1409
1574
|
"survey.topolo.app"
|
|
1410
1575
|
],
|
|
1411
|
-
"routes": [
|
|
1412
|
-
"survey.topolo.app"
|
|
1413
|
-
],
|
|
1576
|
+
"routes": [],
|
|
1414
1577
|
"serviceId": "survey"
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
"name": "topolo-survey-api",
|
|
1581
|
+
"kind": "worker",
|
|
1582
|
+
"apiUrl": null,
|
|
1583
|
+
"domains": [],
|
|
1584
|
+
"routes": [],
|
|
1585
|
+
"serviceId": null
|
|
1415
1586
|
}
|
|
1416
1587
|
]
|
|
1417
1588
|
},
|
|
@@ -1432,10 +1603,16 @@ var APPLICATIONS = {
|
|
|
1432
1603
|
"domains": [
|
|
1433
1604
|
"voice.topolo.app"
|
|
1434
1605
|
],
|
|
1435
|
-
"routes": [
|
|
1436
|
-
"voice.topolo.app"
|
|
1437
|
-
],
|
|
1606
|
+
"routes": [],
|
|
1438
1607
|
"serviceId": "voice"
|
|
1608
|
+
},
|
|
1609
|
+
{
|
|
1610
|
+
"name": "topolo-voice-api",
|
|
1611
|
+
"kind": "worker",
|
|
1612
|
+
"apiUrl": null,
|
|
1613
|
+
"domains": [],
|
|
1614
|
+
"routes": [],
|
|
1615
|
+
"serviceId": null
|
|
1439
1616
|
}
|
|
1440
1617
|
]
|
|
1441
1618
|
},
|
|
@@ -1466,13 +1643,16 @@ var APPLICATIONS = {
|
|
|
1466
1643
|
"domains": [
|
|
1467
1644
|
"sites.topolo.app"
|
|
1468
1645
|
],
|
|
1469
|
-
"routes": [
|
|
1646
|
+
"routes": [
|
|
1647
|
+
"*.sites.topolo.app/*",
|
|
1648
|
+
"sites.topolo.app"
|
|
1649
|
+
],
|
|
1470
1650
|
"serviceId": null
|
|
1471
1651
|
},
|
|
1472
1652
|
{
|
|
1473
1653
|
"name": "topolo-web-studio",
|
|
1474
1654
|
"kind": "worker",
|
|
1475
|
-
"apiUrl":
|
|
1655
|
+
"apiUrl": "https://web.topolo.app",
|
|
1476
1656
|
"domains": [
|
|
1477
1657
|
"web.topolo.app"
|
|
1478
1658
|
],
|
|
@@ -1560,11 +1740,13 @@ var APPLICATION_REQUIREMENTS = [
|
|
|
1560
1740
|
evidence: [
|
|
1561
1741
|
"Auth service catalog contains the service ID and slug",
|
|
1562
1742
|
"Permissions, role bundles, and API-key scopes exist for the service",
|
|
1563
|
-
"SDK, CLI, and MCP service discovery include the callable production API"
|
|
1743
|
+
"SDK, CLI, and MCP service discovery include the callable production API",
|
|
1744
|
+
"CloudControl production HTTP targets classify agent reachability with sdk_service_id or agent_callable:false"
|
|
1564
1745
|
],
|
|
1565
1746
|
implementation: [
|
|
1566
|
-
"Add production Worker api_url metadata
|
|
1567
|
-
"
|
|
1747
|
+
"Add production Worker api_url metadata and sdk_service_id for every agent-callable API target",
|
|
1748
|
+
"Mark browser, documentation, and runtime-only Worker targets with agent_callable:false",
|
|
1749
|
+
"Expose stable service IDs through generated DEFAULT_SERVICE_URLS and live catalog discovery",
|
|
1568
1750
|
"Keep typed SDK/CLI/MCP surfaces in sync when an API contract stabilizes"
|
|
1569
1751
|
]
|
|
1570
1752
|
},
|
|
@@ -1668,8 +1850,10 @@ var APPLICATION_REQUIREMENTS = [
|
|
|
1668
1850
|
function applicationRequirementScopes(application) {
|
|
1669
1851
|
const scopes = /* @__PURE__ */ new Set(["all"]);
|
|
1670
1852
|
const appId = application.id;
|
|
1671
|
-
const hasBrowserSurface = Boolean(application.productionUrl) ||
|
|
1672
|
-
|
|
1853
|
+
const hasBrowserSurface = Boolean(application.productionUrl) || // String() keeps this guard valid after the Pages->Worker migration narrowed
|
|
1854
|
+
// the generated kind union to "worker" (no "pages" literal remains).
|
|
1855
|
+
application.deployTargets.some((target) => String(target.kind) === "pages");
|
|
1856
|
+
const hasApiSurface = application.services.length > 0 || application.deployTargets.some((target) => Boolean(target.serviceId));
|
|
1673
1857
|
if (hasBrowserSurface) scopes.add("browser");
|
|
1674
1858
|
if (hasApiSurface) scopes.add("api");
|
|
1675
1859
|
if (appId === "cli" || appId === "mcp" || appId === "cloudcontrol") {
|
|
@@ -1786,7 +1970,7 @@ function evaluateRequirement(application, requirement) {
|
|
|
1786
1970
|
evidence
|
|
1787
1971
|
);
|
|
1788
1972
|
case "cloudflare-deployment-contract": {
|
|
1789
|
-
if (application.deployTargets.length
|
|
1973
|
+
if (application.deployTargets.length < 1) {
|
|
1790
1974
|
return finding(requirement, "missing", "No CloudControl deploy targets are registered.", evidence);
|
|
1791
1975
|
}
|
|
1792
1976
|
evidence.push(`deployTargets=${application.deployTargets.length}`);
|
|
@@ -1873,6 +2057,7 @@ function resolveServiceUrl(service, overrides) {
|
|
|
1873
2057
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
1874
2058
|
var POSITIVE_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1875
2059
|
var NEGATIVE_CACHE_TTL_MS = 60 * 1e3;
|
|
2060
|
+
var CATALOG_CACHE_TTL_MS = 60 * 1e3;
|
|
1876
2061
|
var TopoloClient = class _TopoloClient {
|
|
1877
2062
|
credential;
|
|
1878
2063
|
agent;
|
|
@@ -1882,6 +2067,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1882
2067
|
timeoutMs;
|
|
1883
2068
|
fetchImpl;
|
|
1884
2069
|
debug;
|
|
2070
|
+
serviceCatalogCache = null;
|
|
1885
2071
|
/**
|
|
1886
2072
|
* Process-wide cache for slug → service-id lookups. Keyed by
|
|
1887
2073
|
* `${authBaseUrl}|${slug}` so multiple `TopoloClient` instances pointing at
|
|
@@ -1927,7 +2113,8 @@ var TopoloClient = class _TopoloClient {
|
|
|
1927
2113
|
`Mutating ${method} requests require { confirm: true }. This guardrail prevents agents from issuing writes without an explicit human-in-the-loop acknowledgement.`
|
|
1928
2114
|
);
|
|
1929
2115
|
}
|
|
1930
|
-
const
|
|
2116
|
+
const resolvedService = await this.resolveRequestService(opts.service);
|
|
2117
|
+
const baseUrl = resolvedService.baseUrl;
|
|
1931
2118
|
const url = new URL(opts.path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
1932
2119
|
if (opts.query) {
|
|
1933
2120
|
for (const [k, v] of Object.entries(opts.query)) {
|
|
@@ -1937,7 +2124,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1937
2124
|
const headers = new Headers();
|
|
1938
2125
|
headers.set("Accept", "application/json");
|
|
1939
2126
|
if (opts.body !== void 0) headers.set("Content-Type", "application/json");
|
|
1940
|
-
const platformServiceId = resolveConfiguredServiceId(
|
|
2127
|
+
const platformServiceId = resolveConfiguredServiceId(resolvedService.serviceKey, this.serviceIds);
|
|
1941
2128
|
if (platformServiceId) headers.set("X-Service-ID", platformServiceId);
|
|
1942
2129
|
applyAuthHeaders(headers, this.credential);
|
|
1943
2130
|
const requestId = generateRequestId();
|
|
@@ -1950,7 +2137,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1950
2137
|
const signal = opts.signal ? mergeSignals(opts.signal, controller.signal) : controller.signal;
|
|
1951
2138
|
const urlStr = url.toString();
|
|
1952
2139
|
const startedAt = Date.now();
|
|
1953
|
-
this.emit({ phase: "request", method, service:
|
|
2140
|
+
this.emit({ phase: "request", method, service: resolvedService.serviceKey, path: opts.path, url: urlStr, requestId });
|
|
1954
2141
|
let res;
|
|
1955
2142
|
try {
|
|
1956
2143
|
res = await this.fetchImpl(urlStr, {
|
|
@@ -1963,7 +2150,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1963
2150
|
this.emit({
|
|
1964
2151
|
phase: "error",
|
|
1965
2152
|
method,
|
|
1966
|
-
service:
|
|
2153
|
+
service: resolvedService.serviceKey,
|
|
1967
2154
|
path: opts.path,
|
|
1968
2155
|
url: urlStr,
|
|
1969
2156
|
requestId,
|
|
@@ -1982,7 +2169,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1982
2169
|
this.emit({
|
|
1983
2170
|
phase: "error",
|
|
1984
2171
|
method,
|
|
1985
|
-
service:
|
|
2172
|
+
service: resolvedService.serviceKey,
|
|
1986
2173
|
path: opts.path,
|
|
1987
2174
|
url: urlStr,
|
|
1988
2175
|
requestId,
|
|
@@ -1997,12 +2184,12 @@ var TopoloClient = class _TopoloClient {
|
|
|
1997
2184
|
extractRequired(parsed)
|
|
1998
2185
|
);
|
|
1999
2186
|
}
|
|
2000
|
-
throw new TopoloHttpError(
|
|
2187
|
+
throw new TopoloHttpError(resolvedService.serviceKey, opts.path, res.status, parsed);
|
|
2001
2188
|
}
|
|
2002
2189
|
this.emit({
|
|
2003
2190
|
phase: "response",
|
|
2004
2191
|
method,
|
|
2005
|
-
service:
|
|
2192
|
+
service: resolvedService.serviceKey,
|
|
2006
2193
|
path: opts.path,
|
|
2007
2194
|
url: urlStr,
|
|
2008
2195
|
requestId,
|
|
@@ -2011,6 +2198,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
|
*
|
|
@@ -2081,6 +2293,10 @@ var TopoloClient = class _TopoloClient {
|
|
|
2081
2293
|
* authoritative; the snapshot is a floor, not a cap.
|
|
2082
2294
|
*/
|
|
2083
2295
|
async listServices() {
|
|
2296
|
+
const now = Date.now();
|
|
2297
|
+
if (this.serviceCatalogCache && this.serviceCatalogCache.expiresAt > now) {
|
|
2298
|
+
return this.serviceCatalogCache.entries;
|
|
2299
|
+
}
|
|
2084
2300
|
try {
|
|
2085
2301
|
const res = await this.request({ service: "auth", method: "GET", path: "/api/services/catalog" });
|
|
2086
2302
|
const raw = res.data?.services ?? res.services;
|
|
@@ -2101,7 +2317,13 @@ var TopoloClient = class _TopoloClient {
|
|
|
2101
2317
|
launchable: s.launchable === true
|
|
2102
2318
|
});
|
|
2103
2319
|
}
|
|
2104
|
-
if (entries.length > 0)
|
|
2320
|
+
if (entries.length > 0) {
|
|
2321
|
+
this.serviceCatalogCache = {
|
|
2322
|
+
entries,
|
|
2323
|
+
expiresAt: now + CATALOG_CACHE_TTL_MS
|
|
2324
|
+
};
|
|
2325
|
+
return entries;
|
|
2326
|
+
}
|
|
2105
2327
|
}
|
|
2106
2328
|
} catch {
|
|
2107
2329
|
}
|
|
@@ -2166,8 +2388,18 @@ function resolveConfiguredServiceId(slug, serviceIds) {
|
|
|
2166
2388
|
}
|
|
2167
2389
|
return null;
|
|
2168
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
|
+
}
|
|
2169
2401
|
function serviceKeyCandidates(slug) {
|
|
2170
|
-
const normalized = slug
|
|
2402
|
+
const normalized = normalizeLookupKey(slug);
|
|
2171
2403
|
const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
|
|
2172
2404
|
return [.../* @__PURE__ */ new Set([
|
|
2173
2405
|
normalized,
|
|
@@ -2176,6 +2408,33 @@ function serviceKeyCandidates(slug) {
|
|
|
2176
2408
|
withoutTopolo.replace(/-/g, "_")
|
|
2177
2409
|
])];
|
|
2178
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
|
+
}
|
|
2179
2438
|
function describeError(body, fallback) {
|
|
2180
2439
|
if (body && typeof body === "object") {
|
|
2181
2440
|
const maybe = body;
|
|
@@ -3013,12 +3272,12 @@ function formatContact(c) {
|
|
|
3013
3272
|
}
|
|
3014
3273
|
|
|
3015
3274
|
// src/commands/api.ts
|
|
3016
|
-
var
|
|
3275
|
+
var STATIC_SERVICE_IDS = new Set(Object.keys(DEFAULT_SERVICE_URLS));
|
|
3017
3276
|
var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
3018
3277
|
var WRITE_METHODS2 = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
3019
3278
|
function registerApiCommand(program2) {
|
|
3020
3279
|
program2.command("api <service> <method> <path>").description(
|
|
3021
|
-
|
|
3280
|
+
"Generic passthrough to a Topolo service. Run `topolo services --json` to discover callable services."
|
|
3022
3281
|
).option("-d, --data <json>", "JSON body (for POST/PUT/PATCH)").option(
|
|
3023
3282
|
"-q, --query <kv...>",
|
|
3024
3283
|
"Query params as key=value (repeatable), e.g. --query source=cli page=2"
|
|
@@ -3033,22 +3292,17 @@ function registerApiCommand(program2) {
|
|
|
3033
3292
|
);
|
|
3034
3293
|
}
|
|
3035
3294
|
const httpMethod = upperMethod;
|
|
3036
|
-
if (!KNOWN_SERVICES.includes(service)) {
|
|
3037
|
-
throw new Error(
|
|
3038
|
-
`Unknown service "${service}". Known services: ${KNOWN_SERVICES.join(", ")}`
|
|
3039
|
-
);
|
|
3040
|
-
}
|
|
3041
3295
|
if (WRITE_METHODS2.has(httpMethod) && !opts.confirm) {
|
|
3042
3296
|
throw new Error(`Mutating ${httpMethod} requires --confirm.`);
|
|
3043
3297
|
}
|
|
3044
3298
|
const environment = selectedEnvironment();
|
|
3045
3299
|
const config = await readConfig();
|
|
3046
3300
|
const profile = resolvedProfileConfig(config, environment);
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
environment
|
|
3051
|
-
|
|
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
|
+
}
|
|
3052
3306
|
let body = void 0;
|
|
3053
3307
|
if (opts.data) {
|
|
3054
3308
|
try {
|
|
@@ -3079,6 +3333,21 @@ function registerApiCommand(program2) {
|
|
|
3079
3333
|
}
|
|
3080
3334
|
);
|
|
3081
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
|
+
}
|
|
3082
3351
|
function parseQueryPairs(pairs) {
|
|
3083
3352
|
if (!pairs || pairs.length === 0) return void 0;
|
|
3084
3353
|
const out = {};
|
|
@@ -3108,6 +3377,8 @@ import {
|
|
|
3108
3377
|
} from "fs";
|
|
3109
3378
|
import path from "path";
|
|
3110
3379
|
var EVIDENCE_FRESHNESS_MS = 36 * 60 * 60 * 1e3;
|
|
3380
|
+
var REQUIRED_AUTH_CLIENT_VERSION = "1.1.9";
|
|
3381
|
+
var REQUIRED_UI_KIT_VERSION = "0.4.67";
|
|
3111
3382
|
function readEvidenceFile(appRoot) {
|
|
3112
3383
|
const candidate = path.join(appRoot, ".topolo", "evidence.json");
|
|
3113
3384
|
if (!isFile(candidate)) return null;
|
|
@@ -3269,28 +3540,53 @@ function enrichFinding(application, finding2, evidence) {
|
|
|
3269
3540
|
evidence: ["service=auth"]
|
|
3270
3541
|
});
|
|
3271
3542
|
}
|
|
3543
|
+
const usesWorkerRuntime = authEvidence.includes("source=@topolo-io/worker-runtime") || authEvidence.includes("source=topolo-worker-runtime") || authEvidence.includes("source=createTopoloWorkerRuntime");
|
|
3272
3544
|
const usesAuthMiddleware = authEvidence.includes("source=@topolo-io/auth-middleware") || authEvidence.includes("source=topolo-auth-middleware") || authEvidence.includes("source=TopoloAuthMiddleware");
|
|
3273
3545
|
const usesAuthClient = authEvidence.includes("source=@topolo-io/auth-client") || authEvidence.includes("source=topolo-auth-client");
|
|
3274
3546
|
const usesTopoloClient = authEvidence.includes("source=TopoloClient");
|
|
3275
3547
|
const usesAuthValidateFetch = authEvidence.includes("source=auth-validate-fetch");
|
|
3276
|
-
if (
|
|
3548
|
+
if (authEvidence.includes(`dependency-version=@topolo-io/auth-client<${REQUIRED_AUTH_CLIENT_VERSION}`)) {
|
|
3549
|
+
return mergeFinding(finding2, {
|
|
3550
|
+
status: "needs_review",
|
|
3551
|
+
reason: `The app depends on @topolo-io/auth-client below ${REQUIRED_AUTH_CLIENT_VERSION}; public-route auth boot and session-channel fixes require the current shared client.`,
|
|
3552
|
+
evidence: authEvidence,
|
|
3553
|
+
nextActions: [
|
|
3554
|
+
`Update @topolo-io/auth-client to ${REQUIRED_AUTH_CLIENT_VERSION} or the workspace package that resolves to it`,
|
|
3555
|
+
"Regenerate the lockfile without reverting unrelated app changes",
|
|
3556
|
+
"Rerun this audit after the package update"
|
|
3557
|
+
]
|
|
3558
|
+
});
|
|
3559
|
+
}
|
|
3560
|
+
if (authEvidence.includes("auth-boot=blocking-cookie-refresh") && !authEvidence.includes("auth-boot=background-cookie-refresh")) {
|
|
3561
|
+
return mergeFinding(finding2, {
|
|
3562
|
+
status: "needs_review",
|
|
3563
|
+
reason: "The app appears to block all browser routes on auth cookie refresh. Public landing and login routes must render immediately and restore the cookie session in the background.",
|
|
3564
|
+
evidence: authEvidence,
|
|
3565
|
+
nextActions: [
|
|
3566
|
+
"Use authClient.initialize({ cookieRefresh: 'background' }) or initializeSession({ cookieRefresh: 'background' }) on public landing/login routes",
|
|
3567
|
+
"Keep cookieRefresh: 'blocking' for protected workspace routes",
|
|
3568
|
+
"Subscribe to auth:session-updated so background restore updates the visible user state"
|
|
3569
|
+
]
|
|
3570
|
+
});
|
|
3571
|
+
}
|
|
3572
|
+
if (usesWorkerRuntime || usesAuthMiddleware || usesAuthClient || usesTopoloClient || usesAuthValidateFetch) {
|
|
3277
3573
|
return mergeFinding(finding2, {
|
|
3278
3574
|
status: "met",
|
|
3279
|
-
reason: "Canonical Topolo auth wiring is present (auth-middleware verifyRequest, auth-client browser
|
|
3575
|
+
reason: "Canonical Topolo auth wiring is present (worker-runtime, auth-middleware verifyRequest, auth-client browser session controller, or TopoloClient).",
|
|
3280
3576
|
evidence: authEvidence
|
|
3281
3577
|
});
|
|
3282
3578
|
}
|
|
3283
3579
|
return mergeFinding(finding2, {
|
|
3284
3580
|
status: "partial",
|
|
3285
|
-
reason: "Shared Topolo auth dependency is declared but the canonical wiring (verifyRequest /
|
|
3581
|
+
reason: "Shared Topolo auth dependency is declared but the canonical wiring (verifyRequest / createBrowserAuthSessionController / useTopoloAuth / TopoloClient) is not visible.",
|
|
3286
3582
|
evidence: authEvidence
|
|
3287
3583
|
});
|
|
3288
3584
|
}
|
|
3289
3585
|
case "service-registration-and-scopes": {
|
|
3290
|
-
const applicationId = String(application.id);
|
|
3291
3586
|
const declaredServices = application.services ?? [];
|
|
3292
3587
|
if (declaredServices.length === 0) {
|
|
3293
|
-
|
|
3588
|
+
const applicationId = String(application.id);
|
|
3589
|
+
if (applicationId === "cloudcontrol") {
|
|
3294
3590
|
return mergeFinding(finding2, {
|
|
3295
3591
|
status: "met",
|
|
3296
3592
|
reason: "This application is itself a tooling/agent surface and does not register a callable platform service.",
|
|
@@ -3373,6 +3669,18 @@ function enrichFinding(application, finding2, evidence) {
|
|
|
3373
3669
|
});
|
|
3374
3670
|
}
|
|
3375
3671
|
if (shellEvidence.length === 0) return finding2;
|
|
3672
|
+
if (shellEvidence.includes(`dependency-version=@topolo-io/ui-kit<${REQUIRED_UI_KIT_VERSION}`)) {
|
|
3673
|
+
return mergeFinding(finding2, {
|
|
3674
|
+
status: "needs_review",
|
|
3675
|
+
reason: `The app depends on @topolo-io/ui-kit below ${REQUIRED_UI_KIT_VERSION}; launcher catalog cache/dedupe fixes require the current shared kit.`,
|
|
3676
|
+
evidence: shellEvidence,
|
|
3677
|
+
nextActions: [
|
|
3678
|
+
`Update @topolo-io/ui-kit to ${REQUIRED_UI_KIT_VERSION}`,
|
|
3679
|
+
"Regenerate the lockfile without reverting unrelated app changes",
|
|
3680
|
+
"Rerun this audit after the package update"
|
|
3681
|
+
]
|
|
3682
|
+
});
|
|
3683
|
+
}
|
|
3376
3684
|
if (shellEvidence.includes("forbidden=workspace-shell")) {
|
|
3377
3685
|
return mergeFinding(finding2, {
|
|
3378
3686
|
status: "needs_review",
|
|
@@ -3439,7 +3747,7 @@ function enrichFinding(application, finding2, evidence) {
|
|
|
3439
3747
|
}
|
|
3440
3748
|
const text = evidence.source.text;
|
|
3441
3749
|
const hasOrganizationIdColumn = orgEvidence.includes("source=organization_id") || orgEvidence.includes("source=organizationId") || orgEvidence.includes("source=tenantId") || orgEvidence.includes("source=tenant_id");
|
|
3442
|
-
const hasCredentialBoundary = orgEvidence.includes("source=organization-auth-boundary") || text.includes("verifyRequest") || text.includes("
|
|
3750
|
+
const hasCredentialBoundary = orgEvidence.includes("source=organization-auth-boundary") || text.includes("verifyRequest") || text.includes("createBrowserAuthSessionController") || text.includes("useTopoloAuth");
|
|
3443
3751
|
if (hasOrganizationIdColumn && hasCredentialBoundary) {
|
|
3444
3752
|
return mergeFinding(finding2, {
|
|
3445
3753
|
status: "met",
|
|
@@ -3454,9 +3762,9 @@ function enrichFinding(application, finding2, evidence) {
|
|
|
3454
3762
|
});
|
|
3455
3763
|
}
|
|
3456
3764
|
case "agent-and-operator-surfaces": {
|
|
3457
|
-
const applicationId = String(application.id);
|
|
3458
3765
|
const declaredServices = application.services ?? [];
|
|
3459
3766
|
const text = evidence.source.text;
|
|
3767
|
+
const applicationId = String(application.id);
|
|
3460
3768
|
const items = [];
|
|
3461
3769
|
if (declaredServices.length > 0) items.push(`services=${declaredServices.join(",")}`);
|
|
3462
3770
|
if (text.includes("TopoloClient")) items.push("source=TopoloClient");
|
|
@@ -3464,10 +3772,10 @@ function enrichFinding(application, finding2, evidence) {
|
|
|
3464
3772
|
items.push("source=confirm-write-gate");
|
|
3465
3773
|
}
|
|
3466
3774
|
if (text.includes("@topolo/sdk")) items.push("source=@topolo/sdk");
|
|
3467
|
-
if (applicationId === "
|
|
3775
|
+
if (applicationId === "cloudcontrol") {
|
|
3468
3776
|
return mergeFinding(finding2, {
|
|
3469
3777
|
status: "met",
|
|
3470
|
-
reason: "This application is itself an agent/operator surface (
|
|
3778
|
+
reason: "This application is itself an agent/operator surface (CloudControl).",
|
|
3471
3779
|
evidence: [`tooling=${applicationId}`, ...items],
|
|
3472
3780
|
nextActions: []
|
|
3473
3781
|
});
|
|
@@ -3715,15 +4023,24 @@ function sharedAuthEvidence(evidence) {
|
|
|
3715
4023
|
const text = evidence.source.text;
|
|
3716
4024
|
const items = [];
|
|
3717
4025
|
if (deps.has("@topolo-io/auth-client")) items.push("dependency=@topolo-io/auth-client");
|
|
4026
|
+
if (packageDependencyVersions(evidence, "@topolo-io/auth-client").some((version) => isStaleDependencyVersion(version, REQUIRED_AUTH_CLIENT_VERSION))) {
|
|
4027
|
+
items.push(`dependency-version=@topolo-io/auth-client<${REQUIRED_AUTH_CLIENT_VERSION}`);
|
|
4028
|
+
}
|
|
3718
4029
|
if (deps.has("@topolo-io/auth-middleware")) items.push("dependency=@topolo-io/auth-middleware");
|
|
4030
|
+
if (deps.has("@topolo-io/worker-runtime")) items.push("dependency=@topolo-io/worker-runtime");
|
|
3719
4031
|
if (deps.has("@topolo/sdk")) items.push("dependency=@topolo/sdk");
|
|
3720
4032
|
if (text.includes("@topolo-io/auth-client")) items.push("source=@topolo-io/auth-client");
|
|
3721
4033
|
if (text.includes("@topolo-io/auth-middleware")) items.push("source=@topolo-io/auth-middleware");
|
|
4034
|
+
if (text.includes("@topolo-io/worker-runtime")) items.push("source=@topolo-io/worker-runtime");
|
|
4035
|
+
if (text.includes("topolo-worker-runtime")) items.push("source=topolo-worker-runtime");
|
|
4036
|
+
if (text.includes("createTopoloWorkerRuntime")) items.push("source=createTopoloWorkerRuntime");
|
|
3722
4037
|
if (text.includes("topolo-auth-client")) items.push("source=topolo-auth-client");
|
|
3723
4038
|
if (text.includes("topolo-auth-middleware")) items.push("source=topolo-auth-middleware");
|
|
3724
4039
|
if (text.includes("TopoloAuthMiddleware")) items.push("source=TopoloAuthMiddleware");
|
|
3725
4040
|
if (text.includes("TopoloClient")) items.push("source=TopoloClient");
|
|
3726
4041
|
if (text.includes("TopoloOAuth")) items.push("source=TopoloOAuth");
|
|
4042
|
+
if (/\.initialize(?:Session)?\(\s*\)/.test(text)) items.push("auth-boot=blocking-cookie-refresh");
|
|
4043
|
+
if (/cookieRefresh:\s*['"]background['"]/.test(text)) items.push("auth-boot=background-cookie-refresh");
|
|
3727
4044
|
if (/Authorization.*Bearer/i.test(text) && (text.includes("/validate") || text.includes("TOPOLO_AUTH_URL") || text.includes("AUTH_BASE_URL") || text.includes("auth.topolo.app"))) {
|
|
3728
4045
|
items.push("source=auth-validate-fetch");
|
|
3729
4046
|
}
|
|
@@ -3745,6 +4062,9 @@ function sharedShellEvidence(evidence) {
|
|
|
3745
4062
|
const text = evidence.source.text;
|
|
3746
4063
|
const items = [];
|
|
3747
4064
|
if (deps.has("@topolo-io/ui-kit")) items.push("dependency=@topolo-io/ui-kit");
|
|
4065
|
+
if (packageDependencyVersions(evidence, "@topolo-io/ui-kit").some((version) => isStaleDependencyVersion(version, REQUIRED_UI_KIT_VERSION))) {
|
|
4066
|
+
items.push(`dependency-version=@topolo-io/ui-kit<${REQUIRED_UI_KIT_VERSION}`);
|
|
4067
|
+
}
|
|
3748
4068
|
if (text.includes("@topolo-io/ui-kit")) items.push("source=@topolo-io/ui-kit");
|
|
3749
4069
|
if (text.includes("TopoloAppShell")) items.push("source=TopoloAppShell");
|
|
3750
4070
|
if (text.includes(["Topolo", "Workspace", "Shell"].join(""))) items.push("forbidden=workspace-shell");
|
|
@@ -3851,6 +4171,36 @@ function dependencyNames(evidence) {
|
|
|
3851
4171
|
])
|
|
3852
4172
|
);
|
|
3853
4173
|
}
|
|
4174
|
+
function packageDependencyVersions(evidence, packageName) {
|
|
4175
|
+
return unique(
|
|
4176
|
+
evidence.packageJsons.flatMap((record) => {
|
|
4177
|
+
const specs = [
|
|
4178
|
+
record.packageJson.dependencies?.[packageName],
|
|
4179
|
+
record.packageJson.devDependencies?.[packageName],
|
|
4180
|
+
record.packageJson.peerDependencies?.[packageName]
|
|
4181
|
+
];
|
|
4182
|
+
return specs.filter((spec) => typeof spec === "string");
|
|
4183
|
+
})
|
|
4184
|
+
);
|
|
4185
|
+
}
|
|
4186
|
+
function isStaleDependencyVersion(spec, minimumVersion) {
|
|
4187
|
+
if (spec.startsWith("workspace:")) return false;
|
|
4188
|
+
const current = parseDependencyVersion(spec);
|
|
4189
|
+
const minimum = parseDependencyVersion(minimumVersion);
|
|
4190
|
+
if (!current || !minimum) return false;
|
|
4191
|
+
const currentParts = current;
|
|
4192
|
+
const minimumParts = minimum;
|
|
4193
|
+
for (const index of [0, 1, 2]) {
|
|
4194
|
+
if (currentParts[index] < minimumParts[index]) return true;
|
|
4195
|
+
if (currentParts[index] > minimumParts[index]) return false;
|
|
4196
|
+
}
|
|
4197
|
+
return false;
|
|
4198
|
+
}
|
|
4199
|
+
function parseDependencyVersion(spec) {
|
|
4200
|
+
const match = spec.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
4201
|
+
if (!match) return null;
|
|
4202
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
4203
|
+
}
|
|
3854
4204
|
function matchingSystems(repoRoot, application) {
|
|
3855
4205
|
const docsRoot = topoloDocsRoot(repoRoot);
|
|
3856
4206
|
const systemsRoot = path.join(docsRoot, "src", "content", "systems");
|
|
@@ -4204,6 +4554,8 @@ function scaffoldFiles(options) {
|
|
|
4204
4554
|
const { application, target } = options;
|
|
4205
4555
|
const slug = toSlug(application.id);
|
|
4206
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;
|
|
4207
4559
|
const files = [
|
|
4208
4560
|
{
|
|
4209
4561
|
path: `${base}/package.json`,
|
|
@@ -4250,10 +4602,10 @@ function scaffoldFiles(options) {
|
|
|
4250
4602
|
"uses strict mode",
|
|
4251
4603
|
"includes browser DOM types for React and Worker types for API-backed apps"
|
|
4252
4604
|
],
|
|
4253
|
-
content: tsconfigJson(options.api)
|
|
4605
|
+
content: tsconfigJson(options.api, target)
|
|
4254
4606
|
},
|
|
4255
4607
|
{
|
|
4256
|
-
path: `${
|
|
4608
|
+
path: `${browserBase}/vite.config.ts`,
|
|
4257
4609
|
purpose: "Vite browser build configuration.",
|
|
4258
4610
|
requiredSignals: [
|
|
4259
4611
|
"builds the authenticated browser surface",
|
|
@@ -4262,7 +4614,7 @@ function scaffoldFiles(options) {
|
|
|
4262
4614
|
content: viteConfig(target)
|
|
4263
4615
|
},
|
|
4264
4616
|
{
|
|
4265
|
-
path: `${
|
|
4617
|
+
path: `${browserBase}/index.html`,
|
|
4266
4618
|
purpose: "Browser entry document.",
|
|
4267
4619
|
requiredSignals: [
|
|
4268
4620
|
"mounts the React app at #root",
|
|
@@ -4271,7 +4623,7 @@ function scaffoldFiles(options) {
|
|
|
4271
4623
|
content: indexHtml(application.name)
|
|
4272
4624
|
},
|
|
4273
4625
|
{
|
|
4274
|
-
path: `${
|
|
4626
|
+
path: `${browserBase}/src/main.tsx`,
|
|
4275
4627
|
purpose: "React app entrypoint.",
|
|
4276
4628
|
requiredSignals: [
|
|
4277
4629
|
"renders the generated app component",
|
|
@@ -4280,7 +4632,7 @@ function scaffoldFiles(options) {
|
|
|
4280
4632
|
content: mainTsx()
|
|
4281
4633
|
},
|
|
4282
4634
|
{
|
|
4283
|
-
path: `${
|
|
4635
|
+
path: `${browserBase}/src/env.d.ts`,
|
|
4284
4636
|
purpose: "Vite environment variable typing.",
|
|
4285
4637
|
requiredSignals: [
|
|
4286
4638
|
"loads vite/client import.meta.env types",
|
|
@@ -4300,7 +4652,7 @@ function scaffoldFiles(options) {
|
|
|
4300
4652
|
}
|
|
4301
4653
|
] : [],
|
|
4302
4654
|
{
|
|
4303
|
-
path: `${
|
|
4655
|
+
path: `${browserBase}/src/styles.css`,
|
|
4304
4656
|
purpose: "Minimal app-local styles outside the shared Topolo shell.",
|
|
4305
4657
|
requiredSignals: [
|
|
4306
4658
|
"does not fork the shared shell chrome",
|
|
@@ -4311,6 +4663,19 @@ function scaffoldFiles(options) {
|
|
|
4311
4663
|
];
|
|
4312
4664
|
if (target === "platform") {
|
|
4313
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
|
+
},
|
|
4314
4679
|
{
|
|
4315
4680
|
path: `${base}/topolo.cloudcontrol.json`,
|
|
4316
4681
|
purpose: "CloudControl source of truth for deploy targets, routes, bindings, and CI metadata.",
|
|
@@ -4436,7 +4801,7 @@ function scaffoldFiles(options) {
|
|
|
4436
4801
|
if (options.browser) {
|
|
4437
4802
|
if (target === "platform") {
|
|
4438
4803
|
files.push({
|
|
4439
|
-
path: `${
|
|
4804
|
+
path: `${browserBase}/src/service-identity.ts`,
|
|
4440
4805
|
purpose: "Central service identity resolver shared by browser and Worker runtime surfaces.",
|
|
4441
4806
|
requiredSignals: [
|
|
4442
4807
|
"exports the generated app id, display name, and stable Auth service slug",
|
|
@@ -4446,7 +4811,7 @@ function scaffoldFiles(options) {
|
|
|
4446
4811
|
});
|
|
4447
4812
|
}
|
|
4448
4813
|
files.push({
|
|
4449
|
-
path: `${
|
|
4814
|
+
path: `${browserBase}/src/App.tsx`,
|
|
4450
4815
|
purpose: "Authenticated browser surface.",
|
|
4451
4816
|
requiredSignals: [
|
|
4452
4817
|
"imports shared TopoloAppShell from @topolo-io/ui-kit",
|
|
@@ -4463,16 +4828,16 @@ function scaffoldFiles(options) {
|
|
|
4463
4828
|
if (options.api) {
|
|
4464
4829
|
files.push(
|
|
4465
4830
|
{
|
|
4466
|
-
path: `${
|
|
4831
|
+
path: `${apiBase}/wrangler.toml`,
|
|
4467
4832
|
purpose: "Cloudflare Worker runtime configuration.",
|
|
4468
4833
|
requiredSignals: [
|
|
4469
4834
|
"bindings match topolo.cloudcontrol.json or topolo.app.json",
|
|
4470
4835
|
"secrets are referenced by name only"
|
|
4471
4836
|
],
|
|
4472
|
-
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)
|
|
4473
4838
|
},
|
|
4474
4839
|
{
|
|
4475
|
-
path: `${
|
|
4840
|
+
path: `${apiBase}/src/worker.ts`,
|
|
4476
4841
|
purpose: "Protected service boundary.",
|
|
4477
4842
|
requiredSignals: [
|
|
4478
4843
|
"uses @topolo-io/worker-runtime for Worker auth, gatewaying, headers, and request helpers",
|
|
@@ -4487,6 +4852,37 @@ function scaffoldFiles(options) {
|
|
|
4487
4852
|
})
|
|
4488
4853
|
}
|
|
4489
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
|
+
}
|
|
4490
4886
|
}
|
|
4491
4887
|
return files;
|
|
4492
4888
|
}
|
|
@@ -4516,20 +4912,22 @@ function topoloConformanceWorkflow(options) {
|
|
|
4516
4912
|
const docsCheckoutStep = options.appDir === "TopoloDocs" ? [] : [
|
|
4517
4913
|
"",
|
|
4518
4914
|
" - name: Checkout TopoloDocs",
|
|
4915
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4519
4916
|
" uses: actions/checkout@v4",
|
|
4520
4917
|
" with:",
|
|
4521
|
-
" repository: Topolo-io/
|
|
4918
|
+
" repository: Topolo-io/TopoloDocs",
|
|
4522
4919
|
" path: Apps/system/TopoloDocs",
|
|
4523
|
-
" token: ${{
|
|
4920
|
+
" token: ${{ steps.app-token.outputs.token || secrets.TOPOLO_CI_REPO_TOKEN }}"
|
|
4524
4921
|
];
|
|
4525
4922
|
const cliCheckoutStep = options.appDir === "TopoloCLI" || options.appDir === "TopoloCli" ? [] : [
|
|
4526
4923
|
"",
|
|
4527
4924
|
" - name: Checkout Topolo CLI",
|
|
4925
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4528
4926
|
" uses: actions/checkout@v4",
|
|
4529
4927
|
" with:",
|
|
4530
4928
|
" repository: Topolo-io/TopoloCli",
|
|
4531
4929
|
" path: Apps/system/TopoloCLI",
|
|
4532
|
-
" token: ${{
|
|
4930
|
+
" token: ${{ steps.app-token.outputs.token || secrets.TOPOLO_CI_REPO_TOKEN }}"
|
|
4533
4931
|
];
|
|
4534
4932
|
return [
|
|
4535
4933
|
"name: Topolo App Conformance",
|
|
@@ -4547,44 +4945,66 @@ function topoloConformanceWorkflow(options) {
|
|
|
4547
4945
|
"jobs:",
|
|
4548
4946
|
" conformance:",
|
|
4549
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 != '' }}`,
|
|
4550
4951
|
" steps:",
|
|
4551
4952
|
" - name: Checkout application",
|
|
4552
4953
|
" uses: actions/checkout@v4",
|
|
4553
4954
|
" with:",
|
|
4554
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",
|
|
4555
4965
|
...docsCheckoutStep,
|
|
4556
4966
|
"",
|
|
4557
4967
|
" - name: Checkout Topolo SDK",
|
|
4968
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4558
4969
|
" uses: actions/checkout@v4",
|
|
4559
4970
|
" with:",
|
|
4560
4971
|
" repository: Topolo-io/topolo-sdk",
|
|
4561
4972
|
" path: Apps/packages/topolo-sdk",
|
|
4562
|
-
" token: ${{
|
|
4973
|
+
" token: ${{ steps.app-token.outputs.token || secrets.TOPOLO_CI_REPO_TOKEN }}",
|
|
4563
4974
|
...cliCheckoutStep,
|
|
4564
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
|
+
"",
|
|
4565
4981
|
" - name: Setup Node.js",
|
|
4982
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4566
4983
|
" uses: actions/setup-node@v4",
|
|
4567
4984
|
" with:",
|
|
4568
4985
|
" node-version: 20",
|
|
4569
4986
|
"",
|
|
4570
4987
|
" - name: Build Topolo SDK",
|
|
4988
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4571
4989
|
" working-directory: Apps/packages/topolo-sdk",
|
|
4572
4990
|
" run: |",
|
|
4573
4991
|
" npm install --no-audit --no-fund",
|
|
4574
4992
|
" npm run build",
|
|
4575
4993
|
"",
|
|
4576
4994
|
" - name: Build Topolo CLI",
|
|
4995
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4577
4996
|
" working-directory: Apps/system/TopoloCLI",
|
|
4578
4997
|
" run: |",
|
|
4579
4998
|
" npm install --no-audit --no-fund",
|
|
4580
4999
|
" npm run build",
|
|
4581
5000
|
"",
|
|
4582
5001
|
" - name: Run Topolo app conformance audit",
|
|
5002
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4583
5003
|
" run: |",
|
|
4584
5004
|
` node Apps/system/TopoloCLI/dist/index.js apps audit ${options.id} --local --repo-root "$GITHUB_WORKSPACE" --fail-on needs_review --json`,
|
|
4585
5005
|
"",
|
|
4586
5006
|
" - name: Record green CI evidence",
|
|
4587
|
-
` if: \${{ success() }}`,
|
|
5007
|
+
` if: \${{ success() && (steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true') }}`,
|
|
4588
5008
|
` working-directory: ${appPath}`,
|
|
4589
5009
|
" run: |",
|
|
4590
5010
|
` node "$GITHUB_WORKSPACE/Apps/system/TopoloCLI/dist/index.js" apps record-evidence ${options.id} --build --typecheck --test --commit "$GITHUB_SHA"`
|
|
@@ -4598,9 +5018,9 @@ function packageJson(application, target, options) {
|
|
|
4598
5018
|
vite: "^5.4.0"
|
|
4599
5019
|
};
|
|
4600
5020
|
const scripts = {
|
|
4601
|
-
dev: "vite --host 0.0.0.0",
|
|
4602
|
-
build: "vite build",
|
|
4603
|
-
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",
|
|
4604
5024
|
typecheck: "tsc --noEmit"
|
|
4605
5025
|
};
|
|
4606
5026
|
if (options.browser) {
|
|
@@ -4612,27 +5032,30 @@ function packageJson(application, target, options) {
|
|
|
4612
5032
|
if (target === "platform") {
|
|
4613
5033
|
dependencies["@topolo-io/auth-client"] = topoloPackageVersion("@topolo-io/auth-client", target);
|
|
4614
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);
|
|
4615
5037
|
dependencies["@topolo-io/ui-kit"] = topoloPackageVersion("@topolo-io/ui-kit", target);
|
|
4616
5038
|
} else {
|
|
4617
5039
|
dependencies["@topolo/login"] = topoloPackageVersion("@topolo/login", target);
|
|
4618
5040
|
}
|
|
4619
5041
|
}
|
|
4620
5042
|
if (options.api) {
|
|
5043
|
+
devDependencies["@cloudflare/workers-types"] = "^4.20260413.1";
|
|
4621
5044
|
if (target === "platform") {
|
|
4622
5045
|
dependencies["@topolo-io/worker-runtime"] = topoloPackageVersion("@topolo-io/worker-runtime", target);
|
|
4623
|
-
dependencies["@topolo-io/widget-server"] = topoloPackageVersion("@topolo-io/widget-server", target);
|
|
4624
5046
|
dependencies["@topolo/sdk"] = topoloPackageVersion("@topolo/sdk", target);
|
|
5047
|
+
dependencies.hono = "^4";
|
|
4625
5048
|
scripts.deploy = `node -e 'console.error("\\nX Bare deploy is disabled. Use deploy:staging or deploy:production.\\n"); process.exit(1)'`;
|
|
4626
|
-
scripts["deploy:staging"] = "DEPLOY_ENVIRONMENT=staging VITE_TOPOLO_AUTH_URL=https://auth.stg.topolo.us npm run build && wrangler deploy --env staging";
|
|
4627
|
-
scripts["deploy:production"] = "DEPLOY_ENVIRONMENT=production 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";
|
|
4628
5051
|
} else {
|
|
4629
|
-
dependencies["@topolo
|
|
5052
|
+
dependencies["@topolo/sdk"] = topoloPackageVersion("@topolo/sdk", target);
|
|
4630
5053
|
scripts.deploy = "npm run build && wrangler deploy";
|
|
4631
5054
|
}
|
|
4632
5055
|
devDependencies.wrangler = "^4.40.2";
|
|
4633
5056
|
}
|
|
4634
5057
|
if (target === "platform" && hasScaffoldResources(options.resources)) {
|
|
4635
|
-
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";
|
|
4636
5059
|
}
|
|
4637
5060
|
return `${json({
|
|
4638
5061
|
name: application.packageName,
|
|
@@ -4649,7 +5072,7 @@ function topoloPackageVersion(packageName, target) {
|
|
|
4649
5072
|
if (target === "developer") {
|
|
4650
5073
|
if (packageName === "@topolo/login") return "^0.1.0";
|
|
4651
5074
|
if (packageName === "@topolo-io/auth-client") return "^1.0.0";
|
|
4652
|
-
if (packageName === "@topolo
|
|
5075
|
+
if (packageName === "@topolo/sdk") return "^0.4.1";
|
|
4653
5076
|
return "^0.1.0";
|
|
4654
5077
|
}
|
|
4655
5078
|
const platformPackageFolders = {
|
|
@@ -4659,8 +5082,9 @@ function topoloPackageVersion(packageName, target) {
|
|
|
4659
5082
|
"@topolo-io/auth-middleware": "topolo-auth-middleware",
|
|
4660
5083
|
"@topolo-io/worker-runtime": "topolo-worker-runtime",
|
|
4661
5084
|
"@topolo-io/design-tokens": "topolo-design-tokens",
|
|
4662
|
-
"@topolo-io/
|
|
4663
|
-
"@topolo-io/
|
|
5085
|
+
"@topolo-io/onboarding": "topolo-onboarding",
|
|
5086
|
+
"@topolo-io/tour": "topolo-tour",
|
|
5087
|
+
"@topolo-io/ui-kit": "topolo-ui-kit"
|
|
4664
5088
|
};
|
|
4665
5089
|
const folder = platformPackageFolders[packageName];
|
|
4666
5090
|
if (!folder) {
|
|
@@ -4745,7 +5169,7 @@ function readme(application, target, options) {
|
|
|
4745
5169
|
"- `vite.config.ts` includes the standard React plugin plus React runtime dedupe so linked Topolo packages resolve one browser React runtime."
|
|
4746
5170
|
);
|
|
4747
5171
|
if (options.api) {
|
|
4748
|
-
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.");
|
|
4749
5173
|
}
|
|
4750
5174
|
if (hasScaffoldResources(options.resources)) {
|
|
4751
5175
|
lines.push(
|
|
@@ -4789,7 +5213,8 @@ function readme(application, target, options) {
|
|
|
4789
5213
|
return `${lines.join("\n")}
|
|
4790
5214
|
`;
|
|
4791
5215
|
}
|
|
4792
|
-
function tsconfigJson(api) {
|
|
5216
|
+
function tsconfigJson(api, target) {
|
|
5217
|
+
const include = target === "platform" ? ["apps/web/src", ...api ? ["apps/api/src"] : []] : ["src"];
|
|
4793
5218
|
return `${json({
|
|
4794
5219
|
compilerOptions: {
|
|
4795
5220
|
target: "ES2022",
|
|
@@ -4807,9 +5232,9 @@ function tsconfigJson(api) {
|
|
|
4807
5232
|
isolatedModules: true,
|
|
4808
5233
|
noEmit: true,
|
|
4809
5234
|
jsx: "react-jsx",
|
|
4810
|
-
types: ["vite/client"]
|
|
5235
|
+
types: api ? ["vite/client", "@cloudflare/workers-types"] : ["vite/client"]
|
|
4811
5236
|
},
|
|
4812
|
-
include
|
|
5237
|
+
include,
|
|
4813
5238
|
references: []
|
|
4814
5239
|
})}
|
|
4815
5240
|
`;
|
|
@@ -4907,21 +5332,47 @@ function serviceIdentityTs(application) {
|
|
|
4907
5332
|
" return cachedBrowserServiceId;",
|
|
4908
5333
|
"}",
|
|
4909
5334
|
"",
|
|
4910
|
-
"async function fetchServiceIdentity(): Promise<Response> {",
|
|
4911
|
-
" const response = await fetch('/api/service-identity', {",
|
|
4912
|
-
" headers: { Accept: 'application/json' },",
|
|
4913
|
-
" );",
|
|
4914
|
-
" if (!response.ok) {",
|
|
4915
|
-
" throw new Error(`Unable to resolve ${SCAFFOLD_SERVICE_SLUG} service identity`);",
|
|
4916
|
-
" }",
|
|
4917
|
-
" return response;",
|
|
5335
|
+
"async function fetchServiceIdentity(): Promise<Response> {",
|
|
5336
|
+
" const response = await fetch('/api/service-identity', {",
|
|
5337
|
+
" headers: { Accept: 'application/json' },",
|
|
5338
|
+
" );",
|
|
5339
|
+
" if (!response.ok) {",
|
|
5340
|
+
" throw new Error(`Unable to resolve ${SCAFFOLD_SERVICE_SLUG} service identity`);",
|
|
5341
|
+
" }",
|
|
5342
|
+
" return response;",
|
|
5343
|
+
"}",
|
|
5344
|
+
"",
|
|
5345
|
+
"function requireServiceId(value: unknown): string {",
|
|
5346
|
+
" const serviceId = typeof value === 'string' ? value.trim() : '';",
|
|
5347
|
+
" if (!serviceId) {",
|
|
5348
|
+
" throw new Error(`SERVICE_ID is required for ${SCAFFOLD_SERVICE_SLUG}`);",
|
|
5349
|
+
" }",
|
|
5350
|
+
" return serviceId;",
|
|
5351
|
+
"}",
|
|
5352
|
+
"",
|
|
5353
|
+
"export function resolveRuntimeServiceId(env: ServiceIdentityEnv): Promise<string> {",
|
|
5354
|
+
" return Promise.resolve(requireServiceId(env.SERVICE_ID));",
|
|
5355
|
+
"}",
|
|
5356
|
+
""
|
|
5357
|
+
].join("\n");
|
|
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;",
|
|
4918
5369
|
"}",
|
|
4919
5370
|
"",
|
|
4920
5371
|
"function requireServiceId(value: unknown): string {",
|
|
4921
5372
|
" const serviceId = typeof value === 'string' ? value.trim() : '';",
|
|
4922
5373
|
" if (!serviceId) {",
|
|
4923
5374
|
" throw new Error(`SERVICE_ID is required for ${SCAFFOLD_SERVICE_SLUG}`);",
|
|
4924
|
-
"
|
|
5375
|
+
" }",
|
|
4925
5376
|
" return serviceId;",
|
|
4926
5377
|
"}",
|
|
4927
5378
|
"",
|
|
@@ -4998,10 +5449,11 @@ function viteConfig(target) {
|
|
|
4998
5449
|
" process.env.VITE_TOPOLO_AUTH_URL === 'https://auth.stg.topolo.us';",
|
|
4999
5450
|
"",
|
|
5000
5451
|
" return {",
|
|
5452
|
+
" root: fileURLToPath(new URL('.', import.meta.url)),",
|
|
5001
5453
|
" plugins: [react(), ...(isStagingBuild ? [stagingUrlRewrite()] : [])],",
|
|
5002
5454
|
" resolve: {",
|
|
5003
5455
|
" alias: {",
|
|
5004
|
-
" '@topolo-io/auth-client': fileURLToPath(new URL('
|
|
5456
|
+
" '@topolo-io/auth-client': fileURLToPath(new URL('../../node_modules/@topolo-io/auth-client/dist/index.js', import.meta.url)),",
|
|
5005
5457
|
" },",
|
|
5006
5458
|
" dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime'],",
|
|
5007
5459
|
" },",
|
|
@@ -5033,6 +5485,8 @@ function mainTsx() {
|
|
|
5033
5485
|
"import React from 'react';",
|
|
5034
5486
|
"import { createRoot } from 'react-dom/client';",
|
|
5035
5487
|
"import '@topolo-io/design-tokens/tokens.css';",
|
|
5488
|
+
"import '@topolo-io/tour/styles.css';",
|
|
5489
|
+
"import '@topolo-io/onboarding/styles.css';",
|
|
5036
5490
|
"import App from './App';",
|
|
5037
5491
|
"import './styles.css';",
|
|
5038
5492
|
"",
|
|
@@ -5135,16 +5589,19 @@ function platformAppTsx(application, options) {
|
|
|
5135
5589
|
const baselineHeading = isPersonal ? "Shared personal baseline" : "Shared workspace baseline";
|
|
5136
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.";
|
|
5137
5591
|
return [
|
|
5138
|
-
"import { Component, useEffect, useMemo, useState, type ErrorInfo, type ReactNode } from 'react';",
|
|
5592
|
+
"import { Component, useCallback, useEffect, useMemo, useState, type ErrorInfo, type ReactNode } from 'react';",
|
|
5139
5593
|
"import { createBrowserAuthSessionController, type AuthUser } from '@topolo-io/auth-client';",
|
|
5140
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';",
|
|
5141
5597
|
"import { SCAFFOLD_APP_ID, SCAFFOLD_APP_NAME, SCAFFOLD_SERVICE_SLUG, getBrowserAuthBaseUrl, resolveAppServiceId } from './service-identity';",
|
|
5142
5598
|
"",
|
|
5143
5599
|
"// Wire global error/console capture so BugReporter (enabled in TopoloAppShell below)",
|
|
5144
5600
|
"// can attach the last few errors to any submitted bug report.",
|
|
5145
5601
|
"setupBugReporterErrorCapture();",
|
|
5146
5602
|
"",
|
|
5147
|
-
"type NavItem = 'home';",
|
|
5603
|
+
"type NavItem = 'home' | 'getting-started';",
|
|
5604
|
+
"type WorkspaceView = 'workspace' | 'getting-started';",
|
|
5148
5605
|
"type AppRoute = { kind: 'root' } | { kind: 'login' } | { kind: 'callback' };",
|
|
5149
5606
|
"type BrowserAuthModule = ReturnType<typeof createBrowserAuthSessionController>;",
|
|
5150
5607
|
"",
|
|
@@ -5221,7 +5678,17 @@ function platformAppTsx(application, options) {
|
|
|
5221
5678
|
" const [loading, setLoading] = useState(true);",
|
|
5222
5679
|
" const [authError, setAuthError] = useState('');",
|
|
5223
5680
|
" const [loginAttempted, setLoginAttempted] = useState(false);",
|
|
5681
|
+
" const [view, setView] = useState<WorkspaceView>('workspace');",
|
|
5224
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]);",
|
|
5225
5692
|
"",
|
|
5226
5693
|
" useEffect(() => {",
|
|
5227
5694
|
" let active = true;",
|
|
@@ -5244,7 +5711,9 @@ function platformAppTsx(application, options) {
|
|
|
5244
5711
|
" if (!auth) return;",
|
|
5245
5712
|
" let active = true;",
|
|
5246
5713
|
" const syncAuth = async () => {",
|
|
5247
|
-
"
|
|
5714
|
+
" const publicRoute = route.kind === 'root' || route.kind === 'login';",
|
|
5715
|
+
" if (publicRoute) setLoading(false);",
|
|
5716
|
+
" else setLoading(true);",
|
|
5248
5717
|
" setAuthError('');",
|
|
5249
5718
|
"",
|
|
5250
5719
|
" if (route.kind === 'callback') {",
|
|
@@ -5260,7 +5729,9 @@ function platformAppTsx(application, options) {
|
|
|
5260
5729
|
" return;",
|
|
5261
5730
|
" }",
|
|
5262
5731
|
"",
|
|
5263
|
-
" const nextUser = await auth.authClient.initialize(
|
|
5732
|
+
" const nextUser = await auth.authClient.initialize({",
|
|
5733
|
+
" cookieRefresh: publicRoute ? 'background' : 'blocking',",
|
|
5734
|
+
" });",
|
|
5264
5735
|
" if (!active) return;",
|
|
5265
5736
|
" setUser(nextUser);",
|
|
5266
5737
|
" setLoading(false);",
|
|
@@ -5277,6 +5748,16 @@ function platformAppTsx(application, options) {
|
|
|
5277
5748
|
" }, [auth, route.kind]);",
|
|
5278
5749
|
"",
|
|
5279
5750
|
" useEffect(() => {",
|
|
5751
|
+
" if (!auth) return;",
|
|
5752
|
+
" const handleSessionUpdated = () => {",
|
|
5753
|
+
" const nextUser = auth.authClient.getHydratedCurrentUser() || auth.authClient.getTokenIdentity();",
|
|
5754
|
+
" if (nextUser) setUser(nextUser);",
|
|
5755
|
+
" };",
|
|
5756
|
+
" window.addEventListener('auth:session-updated', handleSessionUpdated);",
|
|
5757
|
+
" return () => window.removeEventListener('auth:session-updated', handleSessionUpdated);",
|
|
5758
|
+
" }, [auth]);",
|
|
5759
|
+
"",
|
|
5760
|
+
" useEffect(() => {",
|
|
5280
5761
|
" if (!loading && user && route.kind === 'login') {",
|
|
5281
5762
|
" window.location.replace('/');",
|
|
5282
5763
|
" }",
|
|
@@ -5406,11 +5887,11 @@ function platformAppTsx(application, options) {
|
|
|
5406
5887
|
" orgId={user.activeContextType === 'organization' ? user.activeContextId ?? user.orgId ?? null : null}",
|
|
5407
5888
|
" activeContextType={user.activeContextType}",
|
|
5408
5889
|
" activeContextId={user.activeContextId}",
|
|
5409
|
-
|
|
5410
|
-
` 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' }]}`,
|
|
5411
5892
|
' navigationMode="topbar"',
|
|
5412
|
-
" onNavItemSelect={
|
|
5413
|
-
` title=
|
|
5893
|
+
" onNavItemSelect={handleNavSelect}",
|
|
5894
|
+
` title={view === 'getting-started' ? 'Getting started' : '${escapeSingleQuoted(workspaceTitle)}'}`,
|
|
5414
5895
|
" authUrl={AUTH_URL}",
|
|
5415
5896
|
" getAccessToken={getAccessToken}",
|
|
5416
5897
|
" onSignOut={async () => {",
|
|
@@ -5422,18 +5903,94 @@ function platformAppTsx(application, options) {
|
|
|
5422
5903
|
" enableCommandPalette",
|
|
5423
5904
|
" enableBugReporter",
|
|
5424
5905
|
" >",
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
"
|
|
5429
|
-
|
|
5430
|
-
"
|
|
5431
|
-
"
|
|
5432
|
-
"
|
|
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
|
+
" />",
|
|
5433
5938
|
" </TopoloAppShell>",
|
|
5434
5939
|
" );",
|
|
5435
5940
|
"}",
|
|
5436
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
|
+
"",
|
|
5437
5994
|
"function getRoute(): AppRoute {",
|
|
5438
5995
|
" const pathname = window.location.pathname.replace(/\\/+$/, '') || '/';",
|
|
5439
5996
|
" if (pathname === '/login') return { kind: 'login' };",
|
|
@@ -5586,6 +6143,208 @@ function developerAppTsx(application) {
|
|
|
5586
6143
|
""
|
|
5587
6144
|
].join("\n");
|
|
5588
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
|
+
}
|
|
5589
6348
|
function wranglerToml(application, target, resources, profile) {
|
|
5590
6349
|
const slug = toSlug(application.id);
|
|
5591
6350
|
const workerName = target === "platform" ? `topolo-${slug}` : application.packageName;
|
|
@@ -5627,6 +6386,7 @@ function wranglerToml(application, target, resources, profile) {
|
|
|
5627
6386
|
"",
|
|
5628
6387
|
"[vars]",
|
|
5629
6388
|
'AUTH_BASE_URL = "https://auth.topolo.app"',
|
|
6389
|
+
'NEXUS_GATEWAY_URL = "https://nexus-api.topolo.app"',
|
|
5630
6390
|
runtimeIdentityVar,
|
|
5631
6391
|
""
|
|
5632
6392
|
);
|
|
@@ -5681,6 +6441,7 @@ function wranglerToml(application, target, resources, profile) {
|
|
|
5681
6441
|
"",
|
|
5682
6442
|
"[env.staging.vars]",
|
|
5683
6443
|
'AUTH_BASE_URL = "https://auth.stg.topolo.us"',
|
|
6444
|
+
'NEXUS_GATEWAY_URL = "https://nexus-api.stg.topolo.us"',
|
|
5684
6445
|
runtimeIdentityVar,
|
|
5685
6446
|
""
|
|
5686
6447
|
);
|
|
@@ -5735,8 +6496,8 @@ function platformWorkerTs(application, options) {
|
|
|
5735
6496
|
const resource = resourceConfig(application, options.profile);
|
|
5736
6497
|
const envBindings = [
|
|
5737
6498
|
" AUTH_BASE_URL?: string;",
|
|
6499
|
+
" NEXUS_GATEWAY_URL?: string;",
|
|
5738
6500
|
" SERVICE_ID?: string;",
|
|
5739
|
-
" TOPOLO_SERVICE_API_KEY?: string;",
|
|
5740
6501
|
...options.resources.d1 ? [` ${resource.d1Binding}?: unknown;`] : [],
|
|
5741
6502
|
...options.resources.r2 ? [` ${resource.r2Binding}?: unknown;`] : [],
|
|
5742
6503
|
...options.resources.queue ? [` ${resource.queueBinding}?: { send(message: unknown): Promise<void> };`] : [],
|
|
@@ -5769,8 +6530,9 @@ function platformWorkerTs(application, options) {
|
|
|
5769
6530
|
" }, { status: 200 });"
|
|
5770
6531
|
];
|
|
5771
6532
|
return [
|
|
5772
|
-
"import {
|
|
5773
|
-
"import {
|
|
6533
|
+
"import { Hono } from 'hono';",
|
|
6534
|
+
"import { createTopoloWidgetResponse, type TopoloWidgetDraft } from '@topolo/sdk';",
|
|
6535
|
+
"import { createTopoloWorkerRuntime, type TopoloRuntimeContext } from '@topolo-io/worker-runtime';",
|
|
5774
6536
|
"import { SCAFFOLD_APP_ID, SCAFFOLD_APP_NAME, SCAFFOLD_SERVICE_SLUG, resolveRuntimeServiceId } from './service-identity';",
|
|
5775
6537
|
...options.resources.workflow ? ["import { WorkflowEntrypoint, type WorkflowEvent, type WorkflowStep } from 'cloudflare:workers';"] : [],
|
|
5776
6538
|
"",
|
|
@@ -5778,18 +6540,8 @@ function platformWorkerTs(application, options) {
|
|
|
5778
6540
|
...envBindings,
|
|
5779
6541
|
"}",
|
|
5780
6542
|
"",
|
|
5781
|
-
"
|
|
5782
|
-
"
|
|
5783
|
-
"// is installed. Replace the quick-actions placeholder with org-scoped",
|
|
5784
|
-
"// stats once your data model is ready: ctx.user / ctx.orgId / ctx.orgSlug",
|
|
5785
|
-
"// / ctx.isPlatformAdmin / ctx.contextType / ctx.householdId are all",
|
|
5786
|
-
"// pre-resolved. Builder exceptions degrade to empty widgets, not 500s.",
|
|
5787
|
-
"const widgetHandler = createWidgetHandler<Env>({",
|
|
5788
|
-
" serviceId: SCAFFOLD_SERVICE_SLUG,",
|
|
5789
|
-
` serviceName: '${escapeSingleQuoted(serviceName)}',`,
|
|
5790
|
-
" authBaseUrl: (env) => env.AUTH_BASE_URL,",
|
|
5791
|
-
" serviceApiKey: (env) => env.TOPOLO_SERVICE_API_KEY,",
|
|
5792
|
-
" build: () => [",
|
|
6543
|
+
"function buildWidgets(): TopoloWidgetDraft[] {",
|
|
6544
|
+
" return [",
|
|
5793
6545
|
" {",
|
|
5794
6546
|
" type: 'quick-actions',",
|
|
5795
6547
|
" actions: [",
|
|
@@ -5797,18 +6549,25 @@ function platformWorkerTs(application, options) {
|
|
|
5797
6549
|
" { id: 'view-health', label: 'View health', link: '/api/health' },",
|
|
5798
6550
|
" ],",
|
|
5799
6551
|
" },",
|
|
5800
|
-
" ]
|
|
5801
|
-
"}
|
|
6552
|
+
" ];",
|
|
6553
|
+
"}",
|
|
5802
6554
|
"",
|
|
5803
6555
|
"const runtime = createTopoloWorkerRuntime<Env>({",
|
|
5804
6556
|
" appName: SCAFFOLD_APP_NAME,",
|
|
5805
6557
|
" serviceSlug: SCAFFOLD_SERVICE_SLUG,",
|
|
5806
6558
|
" resolveServiceId: (env) => resolveRuntimeServiceId(env),",
|
|
5807
6559
|
" authBaseUrl: (env) => env.AUTH_BASE_URL,",
|
|
5808
|
-
"
|
|
6560
|
+
" usageMonitoring: {",
|
|
6561
|
+
" appId: SCAFFOLD_APP_ID,",
|
|
6562
|
+
" nexusGatewayUrl: (env) => env.NEXUS_GATEWAY_URL,",
|
|
6563
|
+
" },",
|
|
5809
6564
|
"});",
|
|
5810
6565
|
"",
|
|
5811
|
-
"
|
|
6566
|
+
"async function handleRequest(",
|
|
6567
|
+
" _request: Request,",
|
|
6568
|
+
" env: Env,",
|
|
6569
|
+
" rt: TopoloRuntimeContext<Env>,",
|
|
6570
|
+
"): Promise<Response> {",
|
|
5812
6571
|
" const url = rt.url;",
|
|
5813
6572
|
"",
|
|
5814
6573
|
" let serviceId: string;",
|
|
@@ -5831,9 +6590,15 @@ function platformWorkerTs(application, options) {
|
|
|
5831
6590
|
" }, { status: 200 });",
|
|
5832
6591
|
" }",
|
|
5833
6592
|
"",
|
|
5834
|
-
"
|
|
5835
|
-
"
|
|
5836
|
-
"
|
|
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
|
+
" }",
|
|
5837
6602
|
"",
|
|
5838
6603
|
" const auth = await rt.verifyAuth({ serviceId });",
|
|
5839
6604
|
"",
|
|
@@ -5846,10 +6611,21 @@ function platformWorkerTs(application, options) {
|
|
|
5846
6611
|
" }",
|
|
5847
6612
|
"",
|
|
5848
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);",
|
|
5849
6621
|
"});",
|
|
5850
6622
|
"",
|
|
5851
6623
|
"export default {",
|
|
5852
|
-
" fetch:
|
|
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
|
+
" }),",
|
|
5853
6629
|
"};",
|
|
5854
6630
|
"",
|
|
5855
6631
|
"function errorMessage(error: unknown): string {",
|
|
@@ -5864,7 +6640,7 @@ function developerWorkerTs(application) {
|
|
|
5864
6640
|
const serviceId = application.serviceId ?? application.id;
|
|
5865
6641
|
const serviceName = application.name || "Topolo App";
|
|
5866
6642
|
return [
|
|
5867
|
-
"import {
|
|
6643
|
+
"import { createTopoloWidgetResponse, type TopoloWidgetDraft } from '@topolo/sdk';",
|
|
5868
6644
|
"",
|
|
5869
6645
|
"export interface Env {",
|
|
5870
6646
|
" AUTH_BASE_URL?: string;",
|
|
@@ -5875,17 +6651,8 @@ function developerWorkerTs(application) {
|
|
|
5875
6651
|
`const DEFAULT_SERVICE_ID = '${serviceId}';`,
|
|
5876
6652
|
`const SERVICE_NAME = '${escapeSingleQuoted(serviceName)}';`,
|
|
5877
6653
|
"",
|
|
5878
|
-
"
|
|
5879
|
-
"
|
|
5880
|
-
"// is installed. Replace the quick-actions placeholder with org-scoped",
|
|
5881
|
-
"// stats once your data model is ready: ctx.user / ctx.orgId / ctx.orgSlug",
|
|
5882
|
-
"// / ctx.isPlatformAdmin / ctx.contextType / ctx.householdId are all",
|
|
5883
|
-
"// pre-resolved. Builder exceptions degrade to empty widgets, not 500s.",
|
|
5884
|
-
"const widgetHandler = createWidgetHandler<Env>({",
|
|
5885
|
-
" serviceId: DEFAULT_SERVICE_ID,",
|
|
5886
|
-
" serviceName: SERVICE_NAME,",
|
|
5887
|
-
" authBaseUrl: (env) => env.AUTH_BASE_URL,",
|
|
5888
|
-
" build: () => [",
|
|
6654
|
+
"function buildWidgets(): TopoloWidgetDraft[] {",
|
|
6655
|
+
" return [",
|
|
5889
6656
|
" {",
|
|
5890
6657
|
" type: 'quick-actions',",
|
|
5891
6658
|
" actions: [",
|
|
@@ -5893,8 +6660,8 @@ function developerWorkerTs(application) {
|
|
|
5893
6660
|
" { id: 'view-health', label: 'View health', link: '/api/health' },",
|
|
5894
6661
|
" ],",
|
|
5895
6662
|
" },",
|
|
5896
|
-
" ]
|
|
5897
|
-
"}
|
|
6663
|
+
" ];",
|
|
6664
|
+
"}",
|
|
5898
6665
|
"",
|
|
5899
6666
|
"export default {",
|
|
5900
6667
|
" async fetch(request: Request, env: Env): Promise<Response> {",
|
|
@@ -5904,8 +6671,15 @@ function developerWorkerTs(application) {
|
|
|
5904
6671
|
" return json({ ok: true, service: env.SERVICE_ID ?? DEFAULT_SERVICE_ID }, { status: 200 }, request);",
|
|
5905
6672
|
" }",
|
|
5906
6673
|
"",
|
|
5907
|
-
"
|
|
5908
|
-
"
|
|
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
|
+
" }",
|
|
5909
6683
|
"",
|
|
5910
6684
|
" const auth = await verifyTopoloRequest(request, env);",
|
|
5911
6685
|
" if (!auth.ok) {",
|
|
@@ -5961,6 +6735,126 @@ function developerWorkerTs(application) {
|
|
|
5961
6735
|
""
|
|
5962
6736
|
].join("\n");
|
|
5963
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
|
+
}
|
|
5964
6858
|
function cloudcontrolJson(application, options) {
|
|
5965
6859
|
const slug = toSlug(application.id);
|
|
5966
6860
|
const appPath = firstPartyAppPath(application.appDir, options.audience);
|
|
@@ -6069,16 +6963,16 @@ function cloudcontrolJson(application, options) {
|
|
|
6069
6963
|
{
|
|
6070
6964
|
kind: "worker",
|
|
6071
6965
|
environment: "production",
|
|
6072
|
-
target_name: `topolo-${slug}`,
|
|
6966
|
+
target_name: `topolo-${slug}-api-production`,
|
|
6073
6967
|
cloudflare_account_id: CLOUDFLARE_ACCOUNT_ID,
|
|
6074
|
-
config_path: "wrangler.toml",
|
|
6075
|
-
|
|
6076
|
-
|
|
6968
|
+
config_path: "apps/api/wrangler.toml",
|
|
6969
|
+
workdir: "apps/api",
|
|
6970
|
+
build_command: null,
|
|
6971
|
+
output_dir: null,
|
|
6077
6972
|
production_branch: "main",
|
|
6078
6973
|
healthcheck_path: "/api/health",
|
|
6079
|
-
domains: [
|
|
6080
|
-
routes: [
|
|
6081
|
-
api_url: application.productionUrl,
|
|
6974
|
+
domains: [],
|
|
6975
|
+
routes: [],
|
|
6082
6976
|
service_slug: serviceSlug
|
|
6083
6977
|
},
|
|
6084
6978
|
{
|
|
@@ -6086,36 +6980,73 @@ function cloudcontrolJson(application, options) {
|
|
|
6086
6980
|
environment: "staging",
|
|
6087
6981
|
target_name: `topolo-${slug}-staging`,
|
|
6088
6982
|
cloudflare_account_id: STAGING_CLOUDFLARE_ACCOUNT_ID,
|
|
6089
|
-
config_path: "wrangler.toml",
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
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",
|
|
6093
6988
|
healthcheck_path: "/api/health",
|
|
6094
6989
|
domains: [stagingHost],
|
|
6095
6990
|
routes: [stagingHost],
|
|
6096
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: [],
|
|
6097
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
|
|
6098
7028
|
}
|
|
6099
7029
|
] : [
|
|
6100
7030
|
{
|
|
6101
|
-
kind: "
|
|
7031
|
+
kind: "worker",
|
|
6102
7032
|
environment: "production",
|
|
6103
7033
|
target_name: `topolo-${slug}`,
|
|
6104
7034
|
cloudflare_account_id: CLOUDFLARE_ACCOUNT_ID,
|
|
6105
|
-
config_path:
|
|
7035
|
+
config_path: "wrangler.toml",
|
|
6106
7036
|
build_command: "npm run build",
|
|
6107
7037
|
output_dir: "dist",
|
|
6108
7038
|
production_branch: "main",
|
|
6109
|
-
healthcheck_path:
|
|
7039
|
+
healthcheck_path: "/",
|
|
6110
7040
|
domains: [host],
|
|
6111
7041
|
routes: [host],
|
|
6112
|
-
api_url:
|
|
7042
|
+
api_url: application.productionUrl,
|
|
7043
|
+
agent_callable: false
|
|
6113
7044
|
}
|
|
6114
7045
|
];
|
|
6115
7046
|
return `${json({
|
|
6116
7047
|
name: `topolo-${slug}`,
|
|
6117
7048
|
description: `Canonical documentation for ${toTitle(application.name)} lives in Apps/system/TopoloDocs.`,
|
|
6118
|
-
deploy_method:
|
|
7049
|
+
deploy_method: "wrangler",
|
|
6119
7050
|
ci_provider: "github_actions",
|
|
6120
7051
|
repo_identity: {
|
|
6121
7052
|
provider: "github",
|
|
@@ -6195,7 +7126,7 @@ function systemJson(application, options) {
|
|
|
6195
7126
|
visibility: "internal",
|
|
6196
7127
|
api_contract: {
|
|
6197
7128
|
type: options.api ? "curated" : "none",
|
|
6198
|
-
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`,
|
|
6199
7130
|
notes: apiNotes
|
|
6200
7131
|
},
|
|
6201
7132
|
primary_hosts: application.productionUrl ? [application.productionUrl] : [],
|
|
@@ -6284,17 +7215,19 @@ function internalDocs(application, options) {
|
|
|
6284
7215
|
"",
|
|
6285
7216
|
"## Architecture",
|
|
6286
7217
|
"",
|
|
6287
|
-
"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.",
|
|
6288
|
-
options.api ? `The generated app
|
|
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.`,
|
|
6289
7220
|
"",
|
|
6290
7221
|
"## Runtime Surfaces",
|
|
6291
7222
|
"",
|
|
6292
|
-
`- \`${appPath}/src/App.tsx\``,
|
|
6293
|
-
`- \`${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\``,
|
|
6294
7226
|
"- public landing route: `/`",
|
|
6295
7227
|
"- public sign-in route: `/login`",
|
|
6296
7228
|
"- auth callback route: `/auth/callback`",
|
|
6297
|
-
...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\``,
|
|
6298
7231
|
`- \`${appPath}/topolo.cloudcontrol.json\``,
|
|
6299
7232
|
...options.api && application.serviceId ? [`- \`Apps/system/TopoloAuth/catalog/service-manifests/${application.serviceId}.json\``] : [],
|
|
6300
7233
|
"",
|
|
@@ -6453,6 +7386,12 @@ function serviceManifestJson(application, options) {
|
|
|
6453
7386
|
name: "workspace:write",
|
|
6454
7387
|
description: `Manage ${title} ${resourceLabel} data`,
|
|
6455
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
|
|
6456
7395
|
}
|
|
6457
7396
|
],
|
|
6458
7397
|
apiKeyScopes: [
|
|
@@ -6467,6 +7406,12 @@ function serviceManifestJson(application, options) {
|
|
|
6467
7406
|
name: "workspace.write",
|
|
6468
7407
|
description: `Manage ${title} ${resourceLabel} data`,
|
|
6469
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
|
|
6470
7415
|
}
|
|
6471
7416
|
],
|
|
6472
7417
|
roleBundles: {
|
|
@@ -6881,9 +7826,41 @@ function readRequiredString(value, message) {
|
|
|
6881
7826
|
}
|
|
6882
7827
|
return resolved;
|
|
6883
7828
|
}
|
|
7829
|
+
async function applyProvisionedServiceId(manifestPath, serviceId) {
|
|
7830
|
+
let raw;
|
|
7831
|
+
try {
|
|
7832
|
+
raw = await readFile2(manifestPath, "utf8");
|
|
7833
|
+
} catch {
|
|
7834
|
+
return { changed: false, previous: null };
|
|
7835
|
+
}
|
|
7836
|
+
let json2;
|
|
7837
|
+
try {
|
|
7838
|
+
json2 = JSON.parse(raw);
|
|
7839
|
+
} catch {
|
|
7840
|
+
return { changed: false, previous: null };
|
|
7841
|
+
}
|
|
7842
|
+
const previous = typeof json2.serviceId === "string" ? json2.serviceId : null;
|
|
7843
|
+
if (previous === serviceId) {
|
|
7844
|
+
return { changed: false, previous };
|
|
7845
|
+
}
|
|
7846
|
+
json2.serviceId = serviceId;
|
|
7847
|
+
await writeFile3(manifestPath, `${JSON.stringify(json2, null, 2)}
|
|
7848
|
+
`, "utf8");
|
|
7849
|
+
return { changed: true, previous };
|
|
7850
|
+
}
|
|
6884
7851
|
|
|
6885
7852
|
// src/commands/apps.ts
|
|
7853
|
+
import { join as join3 } from "path";
|
|
6886
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
|
+
};
|
|
6887
7864
|
function registerAppsCommand(program2) {
|
|
6888
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) => {
|
|
6889
7866
|
const mode = resolveOutputMode(opts.json);
|
|
@@ -6894,7 +7871,7 @@ function registerAppsCommand(program2) {
|
|
|
6894
7871
|
fail(err, mode);
|
|
6895
7872
|
}
|
|
6896
7873
|
});
|
|
6897
|
-
apps.command("get <application>").description(
|
|
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) => {
|
|
6898
7875
|
const mode = resolveOutputMode(opts.json);
|
|
6899
7876
|
try {
|
|
6900
7877
|
const app = resolveApplication(application);
|
|
@@ -6981,15 +7958,25 @@ function registerAppsCommand(program2) {
|
|
|
6981
7958
|
});
|
|
6982
7959
|
const writeResult = opts.write ? await writeApplicationScaffold(payload, { repoRoot: opts.repoRoot, force: opts.force }) : null;
|
|
6983
7960
|
const provisionResult = opts.provision ? await provisionApplicationScaffold(payload) : null;
|
|
7961
|
+
let serviceIdWriteBack = null;
|
|
7962
|
+
if (writeResult && provisionResult?.authService?.id) {
|
|
7963
|
+
const manifestFile = writeResult.files.find((f) => f.path.endsWith("topolo.app.json"));
|
|
7964
|
+
if (manifestFile) {
|
|
7965
|
+
const manifestPath = join3(writeResult.root, manifestFile.path);
|
|
7966
|
+
const result = await applyProvisionedServiceId(manifestPath, provisionResult.authService.id);
|
|
7967
|
+
serviceIdWriteBack = { ...result, path: manifestFile.path };
|
|
7968
|
+
}
|
|
7969
|
+
}
|
|
6984
7970
|
const output = {
|
|
6985
7971
|
...payload,
|
|
6986
7972
|
...writeResult ? { write: writeResult } : {},
|
|
6987
|
-
...provisionResult ? { provision: provisionResult } : {}
|
|
7973
|
+
...provisionResult ? { provision: provisionResult } : {},
|
|
7974
|
+
...serviceIdWriteBack ? { serviceIdWriteBack } : {}
|
|
6988
7975
|
};
|
|
6989
7976
|
emit(
|
|
6990
7977
|
output,
|
|
6991
7978
|
mode,
|
|
6992
|
-
() => formatScaffold(payload, writeResult, provisionResult)
|
|
7979
|
+
() => formatScaffold(payload, writeResult, provisionResult, serviceIdWriteBack)
|
|
6993
7980
|
);
|
|
6994
7981
|
} catch (err) {
|
|
6995
7982
|
fail(err, mode);
|
|
@@ -7013,7 +8000,7 @@ function applicationRows() {
|
|
|
7013
8000
|
function resolveApplication(application) {
|
|
7014
8001
|
const id = application.trim().toLowerCase();
|
|
7015
8002
|
if (!APPLICATION_IDS.includes(id)) {
|
|
7016
|
-
throw new Error(`Unknown application "${application}".
|
|
8003
|
+
throw new Error(`Unknown application "${application}". ${formatKnownValues(APPLICATION_IDS, "applications")}`);
|
|
7017
8004
|
}
|
|
7018
8005
|
return APPLICATIONS[id];
|
|
7019
8006
|
}
|
|
@@ -7035,7 +8022,12 @@ function applicationRequirementsPayload(application) {
|
|
|
7035
8022
|
};
|
|
7036
8023
|
}
|
|
7037
8024
|
function applicationAuditPayload(application, options = {}) {
|
|
7038
|
-
const
|
|
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();
|
|
7039
8031
|
if (!options.local) return report;
|
|
7040
8032
|
return enrichApplicationAuditWithLocalEvidence(report, { repoRoot: options.repoRoot });
|
|
7041
8033
|
}
|
|
@@ -7159,6 +8151,7 @@ function formatRows(rows) {
|
|
|
7159
8151
|
return lines.join("\n");
|
|
7160
8152
|
}
|
|
7161
8153
|
function formatApplication(application) {
|
|
8154
|
+
const deployTargets = application.deployTargets;
|
|
7162
8155
|
const lines = [
|
|
7163
8156
|
`ID: ${application.id}`,
|
|
7164
8157
|
`Name: ${application.name}`,
|
|
@@ -7168,10 +8161,10 @@ function formatApplication(application) {
|
|
|
7168
8161
|
`Services: ${application.services.length ? application.services.join(", ") : "-"}`,
|
|
7169
8162
|
"Targets:"
|
|
7170
8163
|
];
|
|
7171
|
-
if (
|
|
8164
|
+
if (deployTargets.length === 0) {
|
|
7172
8165
|
lines.push(" - none");
|
|
7173
8166
|
} else {
|
|
7174
|
-
for (const target of
|
|
8167
|
+
for (const target of deployTargets) {
|
|
7175
8168
|
const parts = [`${target.name} (${target.kind})`];
|
|
7176
8169
|
if (target.serviceId) parts.push(`service=${target.serviceId}`);
|
|
7177
8170
|
if (target.apiUrl) parts.push(`api=${target.apiUrl}`);
|
|
@@ -7223,7 +8216,7 @@ function formatAudit(payload) {
|
|
|
7223
8216
|
}
|
|
7224
8217
|
return lines.join("\n");
|
|
7225
8218
|
}
|
|
7226
|
-
function formatScaffold(payload, writeResult = null, provisionResult = null) {
|
|
8219
|
+
function formatScaffold(payload, writeResult = null, provisionResult = null, serviceIdWriteBack = null) {
|
|
7227
8220
|
const lines = [
|
|
7228
8221
|
`Version: ${payload.version}`,
|
|
7229
8222
|
`Target: ${payload.target}`,
|
|
@@ -7283,6 +8276,11 @@ function formatScaffold(payload, writeResult = null, provisionResult = null) {
|
|
|
7283
8276
|
);
|
|
7284
8277
|
}
|
|
7285
8278
|
}
|
|
8279
|
+
if (serviceIdWriteBack) {
|
|
8280
|
+
lines.push(
|
|
8281
|
+
serviceIdWriteBack.changed ? ` - service id written back to ${serviceIdWriteBack.path} (was ${serviceIdWriteBack.previous ?? "unset"})` : ` - service id already current in ${serviceIdWriteBack.path}`
|
|
8282
|
+
);
|
|
8283
|
+
}
|
|
7286
8284
|
return lines.join("\n");
|
|
7287
8285
|
}
|
|
7288
8286
|
function resolveScaffoldAudience(value) {
|
|
@@ -7327,6 +8325,12 @@ function normalizeFailOn(value) {
|
|
|
7327
8325
|
}
|
|
7328
8326
|
throw new Error("Invalid --fail-on value. Use missing, needs_review, or partial.");
|
|
7329
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
|
+
}
|
|
7330
8334
|
function statusesAtOrWorse(threshold) {
|
|
7331
8335
|
if (threshold === "missing") return ["missing"];
|
|
7332
8336
|
if (threshold === "needs_review") return ["missing", "needs_review"];
|
|
@@ -7915,7 +8919,7 @@ function normalizeList(values, fallback = []) {
|
|
|
7915
8919
|
|
|
7916
8920
|
// src/commands/integrations.ts
|
|
7917
8921
|
import { readdir, readFile as readFile3, stat } from "fs/promises";
|
|
7918
|
-
import { join as
|
|
8922
|
+
import { join as join4, relative as relative2 } from "path";
|
|
7919
8923
|
function registerIntegrationsCommand(program2) {
|
|
7920
8924
|
const integrations = program2.command("integrations").description("Generate and validate Topolo integration handoffs.");
|
|
7921
8925
|
integrations.command("prompt").description("Print an agent prompt for adding a Topolo integration.").argument("<integration>", "Integration prompt id, currently login-with-topolo").option("--framework <framework>", "auto, authjs, express, or generic", "auto").option("--json", "Emit JSON output").action((integration, options) => {
|
|
@@ -7973,7 +8977,7 @@ ${file.content}`).join("\n");
|
|
|
7973
8977
|
makeCheck(
|
|
7974
8978
|
"package",
|
|
7975
8979
|
"@topolo/login dependency",
|
|
7976
|
-
packageJson2
|
|
8980
|
+
topoloLoginPackageEvidence(packageJson2, files),
|
|
7977
8981
|
true
|
|
7978
8982
|
),
|
|
7979
8983
|
makeCheck(
|
|
@@ -8025,6 +9029,25 @@ function makeCheck(id, label, evidence, required) {
|
|
|
8025
9029
|
function matchEvidence(haystack, needles) {
|
|
8026
9030
|
return needles.filter((needle) => haystack.toLowerCase().includes(needle.toLowerCase()));
|
|
8027
9031
|
}
|
|
9032
|
+
function topoloLoginPackageEvidence(rootPackageJson, files) {
|
|
9033
|
+
const evidence = [];
|
|
9034
|
+
if (declaresTopoloLogin(rootPackageJson)) evidence.push("package.json declares @topolo/login");
|
|
9035
|
+
for (const file of files) {
|
|
9036
|
+
if (!file.path.endsWith("package.json") || file.path === "package.json") continue;
|
|
9037
|
+
try {
|
|
9038
|
+
if (declaresTopoloLogin(JSON.parse(file.content))) {
|
|
9039
|
+
evidence.push(`${file.path} declares @topolo/login`);
|
|
9040
|
+
}
|
|
9041
|
+
} catch {
|
|
9042
|
+
}
|
|
9043
|
+
}
|
|
9044
|
+
return evidence;
|
|
9045
|
+
}
|
|
9046
|
+
function declaresTopoloLogin(packageJson2) {
|
|
9047
|
+
return Boolean(
|
|
9048
|
+
packageJson2?.dependencies?.["@topolo/login"] || packageJson2?.devDependencies?.["@topolo/login"] || packageJson2?.peerDependencies?.["@topolo/login"]
|
|
9049
|
+
);
|
|
9050
|
+
}
|
|
8028
9051
|
function detectFramework(packageJson2, haystack) {
|
|
8029
9052
|
const deps = {
|
|
8030
9053
|
...packageJson2?.dependencies ?? {},
|
|
@@ -8039,7 +9062,7 @@ function detectFramework(packageJson2, haystack) {
|
|
|
8039
9062
|
}
|
|
8040
9063
|
async function readPackageJson2(repoRoot) {
|
|
8041
9064
|
try {
|
|
8042
|
-
return JSON.parse(await readFile3(
|
|
9065
|
+
return JSON.parse(await readFile3(join4(repoRoot, "package.json"), "utf8"));
|
|
8043
9066
|
} catch {
|
|
8044
9067
|
return null;
|
|
8045
9068
|
}
|
|
@@ -8051,7 +9074,7 @@ async function collectFiles(repoRoot) {
|
|
|
8051
9074
|
if (result.length > 2e3) return;
|
|
8052
9075
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
8053
9076
|
for (const entry of entries) {
|
|
8054
|
-
const fullPath =
|
|
9077
|
+
const fullPath = join4(dir, entry.name);
|
|
8055
9078
|
if (entry.isDirectory()) {
|
|
8056
9079
|
if ([".git", "node_modules", "dist", "build", ".next", "coverage"].includes(entry.name)) continue;
|
|
8057
9080
|
await walk(fullPath);
|