@topolo/cli 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1687 -215
  2. 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
- // ../packages/topolo-sdk/dist/index.js
12
+ // node_modules/@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);
@@ -55,43 +55,78 @@ var TopoloHttpError = class extends TopoloSdkError {
55
55
  body;
56
56
  service;
57
57
  path;
58
- constructor(service, path2, status, body, message) {
59
- super("http_error", message ?? `HTTP ${status} from ${service}${path2}`);
58
+ constructor(service, path4, status, body, message) {
59
+ super("http_error", message ?? `HTTP ${status} from ${service}${path4}`);
60
60
  this.name = "TopoloHttpError";
61
61
  this.status = status;
62
62
  this.body = body;
63
63
  this.service = service;
64
- this.path = path2;
64
+ this.path = path4;
65
65
  }
66
66
  };
67
67
  var SERVICES = {
68
- agent: "https://topolo-agent-worker.topolo.workers.dev",
68
+ agent: "https://agent-api.topolo.app",
69
69
  auth: "https://auth.topolo.app",
70
+ books: "https://books.topolo.app",
70
71
  bugfix: "https://bugfix.topolo.app",
71
- bytes: "https://r2-media-manager-worker.topolo.workers.dev",
72
+ bytes: "https://bytes-api.topolo.app",
72
73
  calendar: "https://calendar.topolo.app",
74
+ capacity: "https://capacity.topolo.app",
73
75
  chat: "https://chat.topolo.app",
74
- commerce: "https://topolo-commerce-api.topolo.workers.dev",
76
+ commerce: "https://commerce-api.topolo.app",
77
+ compose: "https://compose.topolo.app",
75
78
  crm: "https://crm-api.topolo.app",
76
- feed: "https://topolo-feed-api.topolo.app",
77
- forecast: "https://forecast-worker-production.topolo.workers.dev",
78
- learn: "https://topololearn-api.topolo.workers.dev",
79
+ customer_success: "https://success.topolo.app",
80
+ director: "https://director.topolo.app",
81
+ feed: "https://feed-api.topolo.app",
82
+ flow: "https://flow.topolo.app",
83
+ forecast: "https://forecast-api.topolo.app",
84
+ forms: "https://forms.topolo.app",
85
+ insights: "https://insights.topolo.app",
86
+ inventory: "https://inventory.topolo.app",
87
+ learn: "https://learn-api.topolo.app",
88
+ localize: "https://localize.topolo.app",
79
89
  mail: "https://mail.topolo.app",
80
- mdm: "https://topolo-mdm-api.topolo.app",
90
+ mdm: "https://mdm-api.topolo.app",
81
91
  messaging: "https://messages.topolo.app",
82
- nexus: "https://topolo-nexus-gateway.topolo.workers.dev",
92
+ nexus: "https://nexus-api.topolo.app",
93
+ notify: "https://notify.topolo.app",
83
94
  one: "https://one.topolo.app",
84
- pay: "https://topolo-pay.topolo.workers.dev",
95
+ "one.ai": "https://ai.one.topolo.app",
96
+ p2p: "https://p2p.topolo.app",
97
+ pay: "https://pay.topolo.app",
98
+ people: "https://people.topolo.app",
85
99
  "quro.api": "https://api.ol0.me",
86
100
  "quro.redirect": "https://ol0.me",
87
101
  roadmapper: "https://roadmapper-api.topolo.app",
88
- social_studio: "https://topolo-social-studio-api.topolo.workers.dev",
89
- socialize: "https://socialize-prod-api.topolo.workers.dev",
102
+ sign: "https://sign.topolo.app",
103
+ social_studio: "https://studio-api.topolo.app",
104
+ socialize: "https://socialize-api.topolo.app",
105
+ status: "https://status.topolo.app",
90
106
  support: "https://support.topolo.app",
107
+ survey: "https://survey.topolo.app",
108
+ voice: "https://voice.topolo.app",
91
109
  web: "https://web-api.topolo.app"
92
110
  };
93
111
  var PLATFORM_SERVICE_IDS = {
94
- crm: "srv_iCwM4jGXcwlj"
112
+ books: "srv_topolo_books",
113
+ bugfix: "srv_topolo_bugfix",
114
+ compose: "srv_topolo_compose",
115
+ crm: "srv_iCwM4jGXcwlj",
116
+ customer_success: "srv_topolo_success",
117
+ director: "srv_topolo_director",
118
+ flow: "srv_topolo_flow",
119
+ forms: "srv_topolo_forms",
120
+ insights: "srv_topolo_insights",
121
+ inventory: "srv_topolo_inventory",
122
+ localize: "srv_topolo_localize",
123
+ notify: "svc_topolo_notify",
124
+ p2p: "srv_topolo_p2p",
125
+ people: "srv_topolo_people",
126
+ sign: "srv_topolo_sign",
127
+ status: "srv_topolo_status",
128
+ survey: "srv_topolo_survey",
129
+ voice: "srv_topolo_voice"
95
130
  };
96
131
  var APPLICATIONS = {
97
132
  "admin": {
@@ -106,7 +141,9 @@ var APPLICATIONS = {
106
141
  "name": "topoloadmin",
107
142
  "kind": "pages",
108
143
  "apiUrl": null,
109
- "domains": [],
144
+ "domains": [
145
+ "admin.topolo.app"
146
+ ],
110
147
  "routes": [],
111
148
  "serviceId": null
112
149
  }
@@ -126,15 +163,19 @@ var APPLICATIONS = {
126
163
  "name": "topolo-agent",
127
164
  "kind": "pages",
128
165
  "apiUrl": null,
129
- "domains": [],
166
+ "domains": [
167
+ "agent.topolo.app"
168
+ ],
130
169
  "routes": [],
131
170
  "serviceId": null
132
171
  },
133
172
  {
134
173
  "name": "topolo-agent-worker",
135
174
  "kind": "worker",
136
- "apiUrl": "https://topolo-agent-worker.topolo.workers.dev",
137
- "domains": [],
175
+ "apiUrl": "https://agent-api.topolo.app",
176
+ "domains": [
177
+ "agent-api.topolo.app"
178
+ ],
138
179
  "routes": [],
139
180
  "serviceId": "agent"
140
181
  }
@@ -164,6 +205,30 @@ var APPLICATIONS = {
164
205
  }
165
206
  ]
166
207
  },
208
+ "books": {
209
+ "id": "books",
210
+ "appDir": "TopoloBooks",
211
+ "name": "topolo-books",
212
+ "packageName": "topolo-books",
213
+ "productionUrl": "https://books.topolo.app",
214
+ "services": [
215
+ "books"
216
+ ],
217
+ "deployTargets": [
218
+ {
219
+ "name": "topolo-books",
220
+ "kind": "worker",
221
+ "apiUrl": "https://books.topolo.app",
222
+ "domains": [
223
+ "books.topolo.app"
224
+ ],
225
+ "routes": [
226
+ "books.topolo.app"
227
+ ],
228
+ "serviceId": "books"
229
+ }
230
+ ]
231
+ },
167
232
  "bugfix": {
168
233
  "id": "bugfix",
169
234
  "appDir": "TopoloBugFix",
@@ -199,18 +264,22 @@ var APPLICATIONS = {
199
264
  ],
200
265
  "deployTargets": [
201
266
  {
202
- "name": "bytes",
267
+ "name": "topolo-bytes",
203
268
  "kind": "pages",
204
269
  "apiUrl": null,
205
- "domains": [],
270
+ "domains": [
271
+ "bytes.topolo.app"
272
+ ],
206
273
  "routes": [],
207
274
  "serviceId": null
208
275
  },
209
276
  {
210
- "name": "r2-media-manager-worker",
277
+ "name": "topolo-bytes-api",
211
278
  "kind": "worker",
212
- "apiUrl": "https://r2-media-manager-worker.topolo.workers.dev",
213
- "domains": [],
279
+ "apiUrl": "https://bytes-api.topolo.app",
280
+ "domains": [
281
+ "bytes-api.topolo.app"
282
+ ],
214
283
  "routes": [],
215
284
  "serviceId": "bytes"
216
285
  }
@@ -240,6 +309,28 @@ var APPLICATIONS = {
240
309
  }
241
310
  ]
242
311
  },
312
+ "capacity": {
313
+ "id": "capacity",
314
+ "appDir": "TopoloCapacity",
315
+ "name": "topolo-capacity",
316
+ "packageName": "topolo-capacity",
317
+ "productionUrl": "https://capacity.topolo.app",
318
+ "services": [
319
+ "capacity"
320
+ ],
321
+ "deployTargets": [
322
+ {
323
+ "name": "topolo-capacity",
324
+ "kind": "worker",
325
+ "apiUrl": "https://capacity.topolo.app",
326
+ "domains": [
327
+ "capacity.topolo.app"
328
+ ],
329
+ "routes": [],
330
+ "serviceId": "capacity"
331
+ }
332
+ ]
333
+ },
243
334
  "chat": {
244
335
  "id": "chat",
245
336
  "appDir": "TopoloChat",
@@ -276,7 +367,7 @@ var APPLICATIONS = {
276
367
  "cloudcontrol": {
277
368
  "id": "cloudcontrol",
278
369
  "appDir": "TopoloCloudControl",
279
- "name": "topolo-cloudcontrol",
370
+ "name": "cloudcontrol",
280
371
  "packageName": "topolo-cloudcontrol",
281
372
  "productionUrl": null,
282
373
  "services": [],
@@ -295,9 +386,9 @@ var APPLICATIONS = {
295
386
  {
296
387
  "name": "topolo-commerce-api",
297
388
  "kind": "worker",
298
- "apiUrl": "https://topolo-commerce-api.topolo.workers.dev",
389
+ "apiUrl": "https://commerce-api.topolo.app",
299
390
  "domains": [
300
- "topolo-commerce-api.topolo.workers.dev"
391
+ "commerce-api.topolo.app"
301
392
  ],
302
393
  "routes": [],
303
394
  "serviceId": "commerce"
@@ -307,8 +398,7 @@ var APPLICATIONS = {
307
398
  "kind": "pages",
308
399
  "apiUrl": null,
309
400
  "domains": [
310
- "guest.topolo.app",
311
- "topolo-commerce-guest-web.pages.dev"
401
+ "guest.topolo.app"
312
402
  ],
313
403
  "routes": [],
314
404
  "serviceId": null
@@ -318,8 +408,7 @@ var APPLICATIONS = {
318
408
  "kind": "pages",
319
409
  "apiUrl": null,
320
410
  "domains": [
321
- "commerce.topolo.app",
322
- "topolo-commerce-ops-web.pages.dev"
411
+ "commerce.topolo.app"
323
412
  ],
324
413
  "routes": [],
325
414
  "serviceId": null
@@ -329,14 +418,37 @@ var APPLICATIONS = {
329
418
  "kind": "pages",
330
419
  "apiUrl": null,
331
420
  "domains": [
332
- "station-commerce.topolo.app",
333
- "topolo-commerce-station-web.pages.dev"
421
+ "station-commerce.topolo.app"
334
422
  ],
335
423
  "routes": [],
336
424
  "serviceId": null
337
425
  }
338
426
  ]
339
427
  },
428
+ "compose": {
429
+ "id": "compose",
430
+ "appDir": "TopoloCompose",
431
+ "name": "topolo-compose",
432
+ "packageName": "@topolo/compose",
433
+ "productionUrl": "https://compose.topolo.app",
434
+ "services": [
435
+ "compose"
436
+ ],
437
+ "deployTargets": [
438
+ {
439
+ "name": "topolo-compose",
440
+ "kind": "worker",
441
+ "apiUrl": "https://compose.topolo.app",
442
+ "domains": [
443
+ "compose.topolo.app"
444
+ ],
445
+ "routes": [
446
+ "compose.topolo.app"
447
+ ],
448
+ "serviceId": "compose"
449
+ }
450
+ ]
451
+ },
340
452
  "crm": {
341
453
  "id": "crm",
342
454
  "appDir": "TopoloCRM",
@@ -348,20 +460,66 @@ var APPLICATIONS = {
348
460
  ],
349
461
  "deployTargets": [
350
462
  {
351
- "name": "topolo-crm-frontend",
463
+ "name": "topolo-crm-api",
464
+ "kind": "worker",
465
+ "apiUrl": "https://crm-api.topolo.app",
466
+ "domains": [
467
+ "crm-api.topolo.app"
468
+ ],
469
+ "routes": [],
470
+ "serviceId": "crm"
471
+ },
472
+ {
473
+ "name": "topolo-crm-app",
352
474
  "kind": "pages",
353
475
  "apiUrl": null,
354
- "domains": [],
476
+ "domains": [
477
+ "crm.topolo.app"
478
+ ],
355
479
  "routes": [],
356
480
  "serviceId": null
357
- },
481
+ }
482
+ ]
483
+ },
484
+ "customer_success": {
485
+ "id": "customer_success",
486
+ "appDir": "TopoloSuccess",
487
+ "name": "topolo-success",
488
+ "packageName": "topolo-success",
489
+ "productionUrl": "https://success.topolo.app",
490
+ "services": [
491
+ "customer_success"
492
+ ],
493
+ "deployTargets": [
358
494
  {
359
- "name": "topolo-crm-worker",
495
+ "name": "topolo-success",
360
496
  "kind": "worker",
361
- "apiUrl": "https://crm-api.topolo.app",
497
+ "apiUrl": "https://success.topolo.app",
498
+ "domains": [
499
+ "success.topolo.app"
500
+ ],
501
+ "routes": [
502
+ "success.topolo.app"
503
+ ],
504
+ "serviceId": "customer_success"
505
+ }
506
+ ]
507
+ },
508
+ "d1_backup": {
509
+ "id": "d1_backup",
510
+ "appDir": "TopoloD1Backup",
511
+ "name": "topolo-d1-backup",
512
+ "packageName": "topolo-d1-backup",
513
+ "productionUrl": null,
514
+ "services": [],
515
+ "deployTargets": [
516
+ {
517
+ "name": "topolo-d1-backup",
518
+ "kind": "worker",
519
+ "apiUrl": null,
362
520
  "domains": [],
363
521
  "routes": [],
364
- "serviceId": "crm"
522
+ "serviceId": null
365
523
  }
366
524
  ]
367
525
  },
@@ -373,6 +531,14 @@ var APPLICATIONS = {
373
531
  "productionUrl": "https://developers.topolo.app",
374
532
  "services": [],
375
533
  "deployTargets": [
534
+ {
535
+ "name": "topolo-catalog-cron",
536
+ "kind": "worker",
537
+ "apiUrl": null,
538
+ "domains": [],
539
+ "routes": [],
540
+ "serviceId": null
541
+ },
376
542
  {
377
543
  "name": "topolo-developers",
378
544
  "kind": "pages",
@@ -385,6 +551,30 @@ var APPLICATIONS = {
385
551
  }
386
552
  ]
387
553
  },
554
+ "director": {
555
+ "id": "director",
556
+ "appDir": "TopoloDirector",
557
+ "name": "topolo-director",
558
+ "packageName": "topolo-director",
559
+ "productionUrl": "https://director.topolo.app",
560
+ "services": [
561
+ "director"
562
+ ],
563
+ "deployTargets": [
564
+ {
565
+ "name": "topolo-director",
566
+ "kind": "worker",
567
+ "apiUrl": "https://director.topolo.app",
568
+ "domains": [
569
+ "director.topolo.app"
570
+ ],
571
+ "routes": [
572
+ "director.topolo.app"
573
+ ],
574
+ "serviceId": "director"
575
+ }
576
+ ]
577
+ },
388
578
  "docs": {
389
579
  "id": "docs",
390
580
  "appDir": "TopoloDocs",
@@ -409,8 +599,8 @@ var APPLICATIONS = {
409
599
  "id": "feed",
410
600
  "appDir": "TopoloFeed",
411
601
  "name": "TopoloFeed",
412
- "packageName": null,
413
- "productionUrl": "https://topolo-feed-api.topolo.app",
602
+ "packageName": "topolo-feed",
603
+ "productionUrl": "https://feed-api.topolo.app",
414
604
  "services": [
415
605
  "feed"
416
606
  ],
@@ -418,29 +608,53 @@ var APPLICATIONS = {
418
608
  {
419
609
  "name": "topolo-feed-analytics-api",
420
610
  "kind": "worker",
421
- "apiUrl": "https://topolo-feed-analytics-api.topolo.app",
611
+ "apiUrl": "https://feed-analytics-api.topolo.app",
422
612
  "domains": [
423
- "topolo-feed-analytics-api.topolo.app"
613
+ "feed-analytics-api.topolo.app"
424
614
  ],
425
615
  "routes": [
426
- "topolo-feed-analytics-api.topolo.app"
616
+ "feed-analytics-api.topolo.app"
427
617
  ],
428
618
  "serviceId": null
429
619
  },
430
620
  {
431
621
  "name": "topolo-feed-api",
432
622
  "kind": "worker",
433
- "apiUrl": "https://topolo-feed-api.topolo.app",
623
+ "apiUrl": "https://feed-api.topolo.app",
434
624
  "domains": [
435
- "topolo-feed-api.topolo.app"
625
+ "feed-api.topolo.app"
436
626
  ],
437
627
  "routes": [
438
- "topolo-feed-api.topolo.app"
628
+ "feed-api.topolo.app"
439
629
  ],
440
630
  "serviceId": "feed"
441
631
  }
442
632
  ]
443
633
  },
634
+ "flow": {
635
+ "id": "flow",
636
+ "appDir": "TopoloFlow",
637
+ "name": "topolo-flow",
638
+ "packageName": "topolo-flow",
639
+ "productionUrl": "https://flow.topolo.app",
640
+ "services": [
641
+ "flow"
642
+ ],
643
+ "deployTargets": [
644
+ {
645
+ "name": "topolo-flow",
646
+ "kind": "worker",
647
+ "apiUrl": "https://flow.topolo.app",
648
+ "domains": [
649
+ "flow.topolo.app"
650
+ ],
651
+ "routes": [
652
+ "flow.topolo.app"
653
+ ],
654
+ "serviceId": "flow"
655
+ }
656
+ ]
657
+ },
444
658
  "forecast": {
445
659
  "id": "forecast",
446
660
  "appDir": "TopoloForecast",
@@ -452,23 +666,99 @@ var APPLICATIONS = {
452
666
  ],
453
667
  "deployTargets": [
454
668
  {
455
- "name": "forecast",
669
+ "name": "topolo-forecast",
456
670
  "kind": "pages",
457
671
  "apiUrl": null,
458
- "domains": [],
672
+ "domains": [
673
+ "forecast.topolo.app"
674
+ ],
459
675
  "routes": [],
460
676
  "serviceId": null
461
677
  },
462
678
  {
463
- "name": "forecast-worker-production",
679
+ "name": "topolo-forecast-api",
464
680
  "kind": "worker",
465
- "apiUrl": "https://forecast-worker-production.topolo.workers.dev",
466
- "domains": [],
681
+ "apiUrl": "https://forecast-api.topolo.app",
682
+ "domains": [
683
+ "forecast-api.topolo.app"
684
+ ],
467
685
  "routes": [],
468
686
  "serviceId": "forecast"
469
687
  }
470
688
  ]
471
689
  },
690
+ "forms": {
691
+ "id": "forms",
692
+ "appDir": "TopoloForms",
693
+ "name": "topolo-forms",
694
+ "packageName": "topolo-forms",
695
+ "productionUrl": "https://forms.topolo.app",
696
+ "services": [
697
+ "forms"
698
+ ],
699
+ "deployTargets": [
700
+ {
701
+ "name": "topolo-forms",
702
+ "kind": "worker",
703
+ "apiUrl": "https://forms.topolo.app",
704
+ "domains": [
705
+ "forms.topolo.app"
706
+ ],
707
+ "routes": [
708
+ "forms.topolo.app"
709
+ ],
710
+ "serviceId": "forms"
711
+ }
712
+ ]
713
+ },
714
+ "insights": {
715
+ "id": "insights",
716
+ "appDir": "TopoloInsights",
717
+ "name": "topolo-insights",
718
+ "packageName": "topolo-insights",
719
+ "productionUrl": "https://insights.topolo.app",
720
+ "services": [
721
+ "insights"
722
+ ],
723
+ "deployTargets": [
724
+ {
725
+ "name": "topolo-insights",
726
+ "kind": "worker",
727
+ "apiUrl": "https://insights.topolo.app",
728
+ "domains": [
729
+ "insights.topolo.app"
730
+ ],
731
+ "routes": [
732
+ "insights.topolo.app"
733
+ ],
734
+ "serviceId": "insights"
735
+ }
736
+ ]
737
+ },
738
+ "inventory": {
739
+ "id": "inventory",
740
+ "appDir": "TopoloInventory",
741
+ "name": "topolo-inventory",
742
+ "packageName": "topolo-inventory",
743
+ "productionUrl": "https://inventory.topolo.app",
744
+ "services": [
745
+ "inventory"
746
+ ],
747
+ "deployTargets": [
748
+ {
749
+ "name": "topolo-inventory",
750
+ "kind": "worker",
751
+ "apiUrl": "https://inventory.topolo.app",
752
+ "domains": [
753
+ "inventory.topolo.app"
754
+ ],
755
+ "routes": [
756
+ "inventory.topolo.app"
757
+ ],
758
+ "serviceId": "inventory"
759
+ }
760
+ ]
761
+ },
472
762
  "learn": {
473
763
  "id": "learn",
474
764
  "appDir": "TopoloLearn",
@@ -480,7 +770,7 @@ var APPLICATIONS = {
480
770
  ],
481
771
  "deployTargets": [
482
772
  {
483
- "name": "topololearn",
773
+ "name": "topolo-learn",
484
774
  "kind": "pages",
485
775
  "apiUrl": null,
486
776
  "domains": [
@@ -490,17 +780,41 @@ var APPLICATIONS = {
490
780
  "serviceId": null
491
781
  },
492
782
  {
493
- "name": "topololearn-api",
783
+ "name": "topolo-learn-api",
494
784
  "kind": "worker",
495
- "apiUrl": "https://topololearn-api.topolo.workers.dev",
785
+ "apiUrl": "https://learn-api.topolo.app",
496
786
  "domains": [
497
- "topololearn-api.topolo.workers.dev"
787
+ "learn-api.topolo.app"
498
788
  ],
499
789
  "routes": [],
500
790
  "serviceId": "learn"
501
791
  }
502
792
  ]
503
793
  },
794
+ "localize": {
795
+ "id": "localize",
796
+ "appDir": "TopoloLocalize",
797
+ "name": "topolo-localize",
798
+ "packageName": "topolo-localize",
799
+ "productionUrl": "https://localize.topolo.app",
800
+ "services": [
801
+ "localize"
802
+ ],
803
+ "deployTargets": [
804
+ {
805
+ "name": "topolo-localize",
806
+ "kind": "worker",
807
+ "apiUrl": "https://localize.topolo.app",
808
+ "domains": [
809
+ "localize.topolo.app"
810
+ ],
811
+ "routes": [
812
+ "localize.topolo.app"
813
+ ],
814
+ "serviceId": "localize"
815
+ }
816
+ ]
817
+ },
504
818
  "mail": {
505
819
  "id": "mail",
506
820
  "appDir": "TopoloMail",
@@ -538,7 +852,7 @@ var APPLICATIONS = {
538
852
  "id": "mdm",
539
853
  "appDir": "TopoloMDM",
540
854
  "name": "TopoloMDM",
541
- "packageName": null,
855
+ "packageName": "topolo-mdm",
542
856
  "productionUrl": "https://mdm.topolo.app",
543
857
  "services": [
544
858
  "mdm"
@@ -547,12 +861,12 @@ var APPLICATIONS = {
547
861
  {
548
862
  "name": "topolo-mdm-api",
549
863
  "kind": "worker",
550
- "apiUrl": "https://topolo-mdm-api.topolo.app",
864
+ "apiUrl": "https://mdm-api.topolo.app",
551
865
  "domains": [
552
- "topolo-mdm-api.topolo.app"
866
+ "mdm-api.topolo.app"
553
867
  ],
554
868
  "routes": [
555
- "topolo-mdm-api.topolo.app"
869
+ "mdm-api.topolo.app"
556
870
  ],
557
871
  "serviceId": "mdm"
558
872
  },
@@ -561,8 +875,7 @@ var APPLICATIONS = {
561
875
  "kind": "pages",
562
876
  "apiUrl": null,
563
877
  "domains": [
564
- "mdm.topolo.app",
565
- "topolo-mdm-console.pages.dev"
878
+ "mdm.topolo.app"
566
879
  ],
567
880
  "routes": [
568
881
  "mdm.topolo.app"
@@ -616,8 +929,7 @@ var APPLICATIONS = {
616
929
  "kind": "pages",
617
930
  "apiUrl": null,
618
931
  "domains": [
619
- "nexus.topolo.app",
620
- "topolo-nexus-dashboard.pages.dev"
932
+ "nexus.topolo.app"
621
933
  ],
622
934
  "routes": [],
623
935
  "serviceId": null
@@ -625,15 +937,48 @@ var APPLICATIONS = {
625
937
  {
626
938
  "name": "topolo-nexus-gateway",
627
939
  "kind": "worker",
628
- "apiUrl": "https://topolo-nexus-gateway.topolo.workers.dev",
940
+ "apiUrl": "https://nexus-api.topolo.app",
629
941
  "domains": [
630
- "topolo-nexus-gateway.topolo.workers.dev"
942
+ "nexus-api.topolo.app"
631
943
  ],
632
944
  "routes": [],
633
945
  "serviceId": "nexus"
634
946
  }
635
947
  ]
636
948
  },
949
+ "notify": {
950
+ "id": "notify",
951
+ "appDir": "TopoloNotify",
952
+ "name": "topolo-notify",
953
+ "packageName": "@topolo/notify",
954
+ "productionUrl": "https://notify.topolo.app",
955
+ "services": [
956
+ "notify"
957
+ ],
958
+ "deployTargets": [
959
+ {
960
+ "name": "topolo-notify",
961
+ "kind": "worker",
962
+ "apiUrl": "https://notify.topolo.app",
963
+ "domains": [
964
+ "notify.topolo.app"
965
+ ],
966
+ "routes": [
967
+ "notify.topolo.app"
968
+ ],
969
+ "serviceId": "notify"
970
+ }
971
+ ]
972
+ },
973
+ "observability_watch": {
974
+ "id": "observability_watch",
975
+ "appDir": "TopoloObservabilityWatch",
976
+ "name": "topolo-observability-watch",
977
+ "packageName": "topolo-observability-watch",
978
+ "productionUrl": null,
979
+ "services": [],
980
+ "deployTargets": []
981
+ },
637
982
  "one": {
638
983
  "id": "one",
639
984
  "appDir": "TopoloOne",
@@ -641,51 +986,83 @@ var APPLICATIONS = {
641
986
  "packageName": "topolo-oneclick-sso",
642
987
  "productionUrl": "https://one.topolo.app",
643
988
  "services": [
644
- "one"
989
+ "one",
990
+ "one.ai"
645
991
  ],
646
992
  "deployTargets": [
647
993
  {
648
994
  "name": "ai-chat-rag",
649
995
  "kind": "worker",
650
- "apiUrl": null,
996
+ "apiUrl": "https://ai.one.topolo.app",
651
997
  "domains": [
652
- "ai-chat-rag.topolo.workers.dev"
998
+ "ai.one.topolo.app"
653
999
  ],
654
1000
  "routes": [],
655
- "serviceId": null
1001
+ "serviceId": "one.ai"
656
1002
  },
657
1003
  {
658
- "name": "topoloone",
1004
+ "name": "topolo-one",
659
1005
  "kind": "pages",
660
1006
  "apiUrl": null,
661
1007
  "domains": [
662
- "oneclick-dashboard.pages.dev"
1008
+ "one.topolo.app"
663
1009
  ],
664
1010
  "routes": [],
665
1011
  "serviceId": null
666
1012
  },
667
1013
  {
668
- "name": "topoloone-website",
1014
+ "name": "topolo-one-website",
669
1015
  "kind": "worker",
670
1016
  "apiUrl": null,
671
1017
  "domains": [
672
- "one.topolo.io"
1018
+ "topolo.io",
1019
+ "www.topolo.io"
673
1020
  ],
674
1021
  "routes": [],
675
1022
  "serviceId": null
676
1023
  },
677
1024
  {
678
- "name": "topoloone-worker",
1025
+ "name": "topolo-one-worker",
679
1026
  "kind": "worker",
680
1027
  "apiUrl": "https://one.topolo.app",
681
- "domains": [],
1028
+ "domains": [
1029
+ "one.topolo.app",
1030
+ "topolo.app",
1031
+ "www.topolo.app"
1032
+ ],
682
1033
  "routes": [
683
- "one.topolo.app/*"
1034
+ "one.topolo.app/*",
1035
+ "topolo.app",
1036
+ "www.topolo.app/*"
684
1037
  ],
685
1038
  "serviceId": "one"
686
1039
  }
687
1040
  ]
688
1041
  },
1042
+ "p2p": {
1043
+ "id": "p2p",
1044
+ "appDir": "TopoloP2P",
1045
+ "name": "topolo-p2p",
1046
+ "packageName": "topolo-p2p",
1047
+ "productionUrl": "https://p2p.topolo.app",
1048
+ "services": [
1049
+ "p2p"
1050
+ ],
1051
+ "deployTargets": [
1052
+ {
1053
+ "name": "topolo-p2p",
1054
+ "kind": "worker",
1055
+ "apiUrl": "https://p2p.topolo.app",
1056
+ "domains": [
1057
+ "p2p.topolo.app"
1058
+ ],
1059
+ "routes": [
1060
+ "p2p.topolo.app"
1061
+ ],
1062
+ "serviceId": "p2p"
1063
+ }
1064
+ ]
1065
+ },
689
1066
  "pay": {
690
1067
  "id": "pay",
691
1068
  "appDir": "TopoloPay",
@@ -699,18 +1076,53 @@ var APPLICATIONS = {
699
1076
  {
700
1077
  "name": "topolo-pay",
701
1078
  "kind": "worker",
702
- "apiUrl": "https://topolo-pay.topolo.workers.dev",
703
- "domains": [],
1079
+ "apiUrl": "https://pay.topolo.app",
1080
+ "domains": [
1081
+ "pay.topolo.app"
1082
+ ],
704
1083
  "routes": [],
705
1084
  "serviceId": "pay"
706
1085
  }
707
1086
  ]
708
1087
  },
1088
+ "people": {
1089
+ "id": "people",
1090
+ "appDir": "TopoloPeople",
1091
+ "name": "topolo-people",
1092
+ "packageName": "topolo-people",
1093
+ "productionUrl": "https://people.topolo.app",
1094
+ "services": [
1095
+ "people"
1096
+ ],
1097
+ "deployTargets": [
1098
+ {
1099
+ "name": "topolo-people",
1100
+ "kind": "worker",
1101
+ "apiUrl": "https://people.topolo.app",
1102
+ "domains": [
1103
+ "people.topolo.app"
1104
+ ],
1105
+ "routes": [
1106
+ "people.topolo.app"
1107
+ ],
1108
+ "serviceId": "people"
1109
+ }
1110
+ ]
1111
+ },
1112
+ "pulse": {
1113
+ "id": "pulse",
1114
+ "appDir": "TopoloPulse",
1115
+ "name": "topolo-pulse",
1116
+ "packageName": "topolo-pulse",
1117
+ "productionUrl": null,
1118
+ "services": [],
1119
+ "deployTargets": []
1120
+ },
709
1121
  "quro": {
710
1122
  "id": "quro",
711
1123
  "appDir": "TopoloQuro",
712
1124
  "name": "TopoloQuro",
713
- "packageName": null,
1125
+ "packageName": "topolo-quro",
714
1126
  "productionUrl": "https://quro.topolo.app",
715
1127
  "services": [
716
1128
  "quro.api",
@@ -721,7 +1133,9 @@ var APPLICATIONS = {
721
1133
  "name": "quro-api",
722
1134
  "kind": "worker",
723
1135
  "apiUrl": "https://api.ol0.me",
724
- "domains": [],
1136
+ "domains": [
1137
+ "api.ol0.me"
1138
+ ],
725
1139
  "routes": [],
726
1140
  "serviceId": "quro.api"
727
1141
  },
@@ -729,7 +1143,9 @@ var APPLICATIONS = {
729
1143
  "name": "quro-redirect-system",
730
1144
  "kind": "worker",
731
1145
  "apiUrl": "https://ol0.me",
732
- "domains": [],
1146
+ "domains": [
1147
+ "ol0.me"
1148
+ ],
733
1149
  "routes": [
734
1150
  "ol0.me/*"
735
1151
  ],
@@ -739,7 +1155,9 @@ var APPLICATIONS = {
739
1155
  "name": "quro-ui",
740
1156
  "kind": "pages",
741
1157
  "apiUrl": null,
742
- "domains": [],
1158
+ "domains": [
1159
+ "quro.topolo.app"
1160
+ ],
743
1161
  "routes": [],
744
1162
  "serviceId": null
745
1163
  }
@@ -756,7 +1174,7 @@ var APPLICATIONS = {
756
1174
  ],
757
1175
  "deployTargets": [
758
1176
  {
759
- "name": "roadmapper",
1177
+ "name": "topolo-roadmapper",
760
1178
  "kind": "pages",
761
1179
  "apiUrl": null,
762
1180
  "domains": [
@@ -766,7 +1184,7 @@ var APPLICATIONS = {
766
1184
  "serviceId": null
767
1185
  },
768
1186
  {
769
- "name": "topolo-production-roadmapper",
1187
+ "name": "topolo-roadmapper-api",
770
1188
  "kind": "worker",
771
1189
  "apiUrl": "https://roadmapper-api.topolo.app",
772
1190
  "domains": [
@@ -777,6 +1195,30 @@ var APPLICATIONS = {
777
1195
  }
778
1196
  ]
779
1197
  },
1198
+ "sign": {
1199
+ "id": "sign",
1200
+ "appDir": "TopoloSign",
1201
+ "name": "topolo-sign",
1202
+ "packageName": "topolo-sign",
1203
+ "productionUrl": "https://sign.topolo.app",
1204
+ "services": [
1205
+ "sign"
1206
+ ],
1207
+ "deployTargets": [
1208
+ {
1209
+ "name": "topolo-sign",
1210
+ "kind": "worker",
1211
+ "apiUrl": "https://sign.topolo.app",
1212
+ "domains": [
1213
+ "sign.topolo.app"
1214
+ ],
1215
+ "routes": [
1216
+ "sign.topolo.app"
1217
+ ],
1218
+ "serviceId": "sign"
1219
+ }
1220
+ ]
1221
+ },
780
1222
  "social_studio": {
781
1223
  "id": "social_studio",
782
1224
  "appDir": "TopoloSocialStudio",
@@ -791,15 +1233,19 @@ var APPLICATIONS = {
791
1233
  "name": "topolo-social-studio",
792
1234
  "kind": "pages",
793
1235
  "apiUrl": null,
794
- "domains": [],
1236
+ "domains": [
1237
+ "studio.topolo.app"
1238
+ ],
795
1239
  "routes": [],
796
1240
  "serviceId": null
797
1241
  },
798
1242
  {
799
1243
  "name": "topolo-social-studio-api",
800
1244
  "kind": "worker",
801
- "apiUrl": "https://topolo-social-studio-api.topolo.workers.dev",
802
- "domains": [],
1245
+ "apiUrl": "https://studio-api.topolo.app",
1246
+ "domains": [
1247
+ "studio-api.topolo.app"
1248
+ ],
803
1249
  "routes": [],
804
1250
  "serviceId": "social_studio"
805
1251
  },
@@ -824,23 +1270,27 @@ var APPLICATIONS = {
824
1270
  ],
825
1271
  "deployTargets": [
826
1272
  {
827
- "name": "socialize",
1273
+ "name": "topolo-socialize",
828
1274
  "kind": "pages",
829
1275
  "apiUrl": null,
830
- "domains": [],
1276
+ "domains": [
1277
+ "socialize.topolo.app"
1278
+ ],
831
1279
  "routes": [],
832
1280
  "serviceId": null
833
1281
  },
834
1282
  {
835
- "name": "socialize-prod-api",
1283
+ "name": "topolo-socialize-api",
836
1284
  "kind": "worker",
837
- "apiUrl": "https://socialize-prod-api.topolo.workers.dev",
838
- "domains": [],
1285
+ "apiUrl": "https://socialize-api.topolo.app",
1286
+ "domains": [
1287
+ "socialize-api.topolo.app"
1288
+ ],
839
1289
  "routes": [],
840
1290
  "serviceId": "socialize"
841
1291
  },
842
1292
  {
843
- "name": "socialize-prod-scheduler",
1293
+ "name": "topolo-socialize-scheduler",
844
1294
  "kind": "worker",
845
1295
  "apiUrl": null,
846
1296
  "domains": [],
@@ -849,6 +1299,30 @@ var APPLICATIONS = {
849
1299
  }
850
1300
  ]
851
1301
  },
1302
+ "status": {
1303
+ "id": "status",
1304
+ "appDir": "TopoloStatus",
1305
+ "name": "topolo-status",
1306
+ "packageName": "topolo-status",
1307
+ "productionUrl": "https://status.topolo.app",
1308
+ "services": [
1309
+ "status"
1310
+ ],
1311
+ "deployTargets": [
1312
+ {
1313
+ "name": "topolo-status",
1314
+ "kind": "worker",
1315
+ "apiUrl": "https://status.topolo.app",
1316
+ "domains": [
1317
+ "status.topolo.app"
1318
+ ],
1319
+ "routes": [
1320
+ "status.topolo.app"
1321
+ ],
1322
+ "serviceId": "status"
1323
+ }
1324
+ ]
1325
+ },
852
1326
  "support": {
853
1327
  "id": "support",
854
1328
  "appDir": "TopoloSupport",
@@ -873,6 +1347,54 @@ var APPLICATIONS = {
873
1347
  }
874
1348
  ]
875
1349
  },
1350
+ "survey": {
1351
+ "id": "survey",
1352
+ "appDir": "TopoloSurvey",
1353
+ "name": "topolo-survey",
1354
+ "packageName": "topolo-survey",
1355
+ "productionUrl": "https://survey.topolo.app",
1356
+ "services": [
1357
+ "survey"
1358
+ ],
1359
+ "deployTargets": [
1360
+ {
1361
+ "name": "topolo-survey",
1362
+ "kind": "worker",
1363
+ "apiUrl": "https://survey.topolo.app",
1364
+ "domains": [
1365
+ "survey.topolo.app"
1366
+ ],
1367
+ "routes": [
1368
+ "survey.topolo.app"
1369
+ ],
1370
+ "serviceId": "survey"
1371
+ }
1372
+ ]
1373
+ },
1374
+ "voice": {
1375
+ "id": "voice",
1376
+ "appDir": "TopoloVoice",
1377
+ "name": "topolo-voice",
1378
+ "packageName": "topolo-voice",
1379
+ "productionUrl": "https://voice.topolo.app",
1380
+ "services": [
1381
+ "voice"
1382
+ ],
1383
+ "deployTargets": [
1384
+ {
1385
+ "name": "topolo-voice",
1386
+ "kind": "worker",
1387
+ "apiUrl": "https://voice.topolo.app",
1388
+ "domains": [
1389
+ "voice.topolo.app"
1390
+ ],
1391
+ "routes": [
1392
+ "voice.topolo.app"
1393
+ ],
1394
+ "serviceId": "voice"
1395
+ }
1396
+ ]
1397
+ },
876
1398
  "web": {
877
1399
  "id": "web",
878
1400
  "appDir": "TopoloWeb",
@@ -884,7 +1406,7 @@ var APPLICATIONS = {
884
1406
  ],
885
1407
  "deployTargets": [
886
1408
  {
887
- "name": "topoloweb-api",
1409
+ "name": "topolo-web-api",
888
1410
  "kind": "worker",
889
1411
  "apiUrl": "https://web-api.topolo.app",
890
1412
  "domains": [
@@ -894,7 +1416,7 @@ var APPLICATIONS = {
894
1416
  "serviceId": "web"
895
1417
  },
896
1418
  {
897
- "name": "topoloweb-runtime",
1419
+ "name": "topolo-web-runtime",
898
1420
  "kind": "worker",
899
1421
  "apiUrl": "https://sites.topolo.app",
900
1422
  "domains": [
@@ -904,7 +1426,7 @@ var APPLICATIONS = {
904
1426
  "serviceId": null
905
1427
  },
906
1428
  {
907
- "name": "topoloweb-studio",
1429
+ "name": "topolo-web-studio",
908
1430
  "kind": "pages",
909
1431
  "apiUrl": null,
910
1432
  "domains": [
@@ -916,7 +1438,7 @@ var APPLICATIONS = {
916
1438
  ]
917
1439
  }
918
1440
  };
919
- var APPLICATION_REQUIREMENTS_VERSION = "2026-04-22.1";
1441
+ var APPLICATION_REQUIREMENTS_VERSION = "2026-05-01.1";
920
1442
  var APPLICATION_REQUIREMENTS = [
921
1443
  {
922
1444
  id: "platform-metadata",
@@ -974,14 +1496,16 @@ var APPLICATION_REQUIREMENTS = [
974
1496
  evidence: [
975
1497
  "@topolo/ui-kit is used for the platform shell where the app has authenticated UI",
976
1498
  "The shared launcher/app switcher is reachable from authenticated app chrome",
977
- "Account menu and sign-out behavior use TopoloAppShell or TopoloAccountMenu"
1499
+ "Account menu and sign-out behavior use TopoloAppShell or TopoloAccountMenu",
1500
+ "Authenticated app body content stays inside the viewport on mobile and desktop widths"
978
1501
  ],
979
1502
  implementation: [
980
1503
  "Use TopoloAppShell for authenticated app layouts",
981
1504
  "Use TopoloAccountMenu when a partial-shell surface still needs account chrome",
982
1505
  "Pass app-specific account actions as additive menu items instead of forking dropdowns",
983
1506
  "Use shared design tokens and platform UI primitives before creating local equivalents",
984
- "Keep mobile and desktop shell behavior aligned with the platform UI kit"
1507
+ "Keep mobile and desktop shell behavior aligned with the platform UI kit",
1508
+ "Constrain app-owned workspace wrappers with min-width: 0, width: auto, and viewport-aware max-width so shell padding cannot create page-level horizontal overflow"
985
1509
  ]
986
1510
  },
987
1511
  {
@@ -1048,6 +1572,22 @@ var APPLICATION_REQUIREMENTS = [
1048
1572
  "Keep command output JSON-stable for agents and human-readable with --no-json"
1049
1573
  ]
1050
1574
  },
1575
+ {
1576
+ id: "native-dashboard-widget",
1577
+ title: "Expose a native dashboard widget contract",
1578
+ summary: "Launchable first-party applications must populate TopoloOne live workspace through an app-owned /api/widget endpoint using the SDK widget response contract.",
1579
+ appliesTo: ["api"],
1580
+ evidence: [
1581
+ "The app-owned API exposes GET /api/widget",
1582
+ "The endpoint returns TopoloWidgetApiResponse from @topolo/sdk",
1583
+ "The endpoint derives organization/user context from the app auth boundary"
1584
+ ],
1585
+ implementation: [
1586
+ "Import createTopoloWidgetResponse from @topolo/sdk in the app backend",
1587
+ "Return native app metrics or action widgets instead of generic launch-card fallbacks",
1588
+ "Add a route-level test that validates the response with validateTopoloWidgetResponse"
1589
+ ]
1590
+ },
1051
1591
  {
1052
1592
  id: "observability-and-audit",
1053
1593
  title: "Emit platform audit and debugging signals",
@@ -1083,9 +1623,8 @@ var APPLICATION_REQUIREMENTS = [
1083
1623
  ];
1084
1624
  function applicationRequirementScopes(application) {
1085
1625
  const scopes = /* @__PURE__ */ new Set(["all"]);
1086
- const deployTargets = application.deployTargets;
1087
- const hasBrowserSurface = Boolean(application.productionUrl) || deployTargets.some((target) => target.kind === "pages");
1088
- const hasApiSurface = application.services.length > 0 || deployTargets.some((target) => Boolean(target.apiUrl));
1626
+ const hasBrowserSurface = Boolean(application.productionUrl) || application.deployTargets.some((target) => target.kind === "pages");
1627
+ const hasApiSurface = application.services.length > 0 || application.deployTargets.some((target) => Boolean(target.apiUrl));
1089
1628
  if (hasBrowserSurface) scopes.add("browser");
1090
1629
  if (hasApiSurface) scopes.add("api");
1091
1630
  if (application.id === "cli" || application.id === "mcp" || application.id === "cloudcontrol") {
@@ -1220,6 +1759,13 @@ function evaluateRequirement(application, requirement) {
1220
1759
  }
1221
1760
  return finding(requirement, "missing", "No SDK service or tooling surface is registered.", evidence);
1222
1761
  }
1762
+ case "native-dashboard-widget":
1763
+ return finding(
1764
+ requirement,
1765
+ "needs_review",
1766
+ "Catalog metadata cannot verify app-owned /api/widget implementation details.",
1767
+ evidence
1768
+ );
1223
1769
  case "observability-and-audit":
1224
1770
  return finding(
1225
1771
  requirement,
@@ -1271,11 +1817,10 @@ function statusRank(status) {
1271
1817
  }
1272
1818
  var DEFAULT_SERVICE_URLS = SERVICES;
1273
1819
  function resolveServiceUrl(service, overrides) {
1274
- var _a;
1275
- const override = overrides == null ? void 0 : overrides[service];
1820
+ const override = overrides?.[service];
1276
1821
  if (override) return override;
1277
1822
  const envKey = `TOPOLO_SERVICE_URL_${service.replace(/\./g, "_").toUpperCase()}`;
1278
- const envValue = typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[envKey] : void 0;
1823
+ const envValue = typeof process !== "undefined" ? process.env?.[envKey] : void 0;
1279
1824
  if (envValue) return envValue;
1280
1825
  return DEFAULT_SERVICE_URLS[service];
1281
1826
  }
@@ -1289,9 +1834,8 @@ var TopoloClient = class {
1289
1834
  fetchImpl;
1290
1835
  debug;
1291
1836
  constructor(options) {
1292
- var _a;
1293
1837
  if (!options.credential) throw new TopoloAuthError("credential is required");
1294
- if (!((_a = options.agent) == null ? void 0 : _a.clientName)) throw new TopoloAuthError("agent.clientName is required");
1838
+ if (!options.agent?.clientName) throw new TopoloAuthError("agent.clientName is required");
1295
1839
  this.credential = options.credential;
1296
1840
  this.agent = options.agent;
1297
1841
  this.serviceUrls = options.serviceUrls;
@@ -1479,7 +2023,6 @@ var CrmModule = class {
1479
2023
  }
1480
2024
  client;
1481
2025
  async listContacts(options = {}) {
1482
- var _a, _b, _c;
1483
2026
  const res = await this.client.request({
1484
2027
  service: "crm",
1485
2028
  path: "/api/contacts",
@@ -1489,12 +2032,12 @@ var CrmModule = class {
1489
2032
  pageSize: options.pageSize
1490
2033
  }
1491
2034
  });
1492
- const rows = Array.isArray(res == null ? void 0 : res.contacts) ? res.contacts : Array.isArray(res == null ? void 0 : res.data) ? res.data : [];
2035
+ const rows = Array.isArray(res?.contacts) ? res.contacts : Array.isArray(res?.data) ? res.data : [];
1493
2036
  return {
1494
2037
  contacts: rows.map(toSummary),
1495
- total: (res == null ? void 0 : res.total) ?? ((_a = res == null ? void 0 : res.pagination) == null ? void 0 : _a.total) ?? rows.length,
1496
- page: (res == null ? void 0 : res.page) ?? ((_b = res == null ? void 0 : res.pagination) == null ? void 0 : _b.page) ?? options.page ?? 1,
1497
- pageSize: (res == null ? void 0 : res.pageSize) ?? ((_c = res == null ? void 0 : res.pagination) == null ? void 0 : _c.pageSize) ?? options.pageSize ?? rows.length
2038
+ total: res?.total ?? res?.pagination?.total ?? rows.length,
2039
+ page: res?.page ?? res?.pagination?.page ?? options.page ?? 1,
2040
+ pageSize: res?.pageSize ?? res?.pagination?.pageSize ?? options.pageSize ?? rows.length
1498
2041
  };
1499
2042
  }
1500
2043
  async getContact(contactId) {
@@ -1588,8 +2131,8 @@ var TopoloOAuth = class {
1588
2131
  if (params.clientSecret) body.set("client_secret", params.clientSecret);
1589
2132
  return this.#postForm("/api/developer-oauth/token", body);
1590
2133
  }
1591
- async #postForm(path2, body) {
1592
- const url = new URL(path2, this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`);
2134
+ async #postForm(path4, body) {
2135
+ const url = new URL(path4, this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`);
1593
2136
  const res = await this.fetchImpl(url.toString(), {
1594
2137
  method: "POST",
1595
2138
  headers: {
@@ -1605,7 +2148,7 @@ var TopoloOAuth = class {
1605
2148
  const err = parsed;
1606
2149
  throw new TopoloAuthError(err.error_description || err.error, err.error);
1607
2150
  }
1608
- throw new TopoloHttpError("auth", path2, res.status, parsed);
2151
+ throw new TopoloHttpError("auth", path4, res.status, parsed);
1609
2152
  }
1610
2153
  return parsed;
1611
2154
  }
@@ -1631,22 +2174,22 @@ function configPath() {
1631
2174
  return join(base, "topolo", "config.json");
1632
2175
  }
1633
2176
  async function readConfig() {
1634
- const path2 = configPath();
1635
- if (!existsSync(path2)) return {};
2177
+ const path4 = configPath();
2178
+ if (!existsSync(path4)) return {};
1636
2179
  try {
1637
- const raw = await readFile(path2, "utf8");
2180
+ const raw = await readFile(path4, "utf8");
1638
2181
  return JSON.parse(raw);
1639
2182
  } catch {
1640
2183
  return {};
1641
2184
  }
1642
2185
  }
1643
2186
  async function writeConfig(config) {
1644
- const path2 = configPath();
1645
- await mkdir(dirname(path2), { recursive: true });
1646
- await writeFile(path2, JSON.stringify(config, null, 2) + "\n", "utf8");
2187
+ const path4 = configPath();
2188
+ await mkdir(dirname(path4), { recursive: true });
2189
+ await writeFile(path4, JSON.stringify(config, null, 2) + "\n", "utf8");
1647
2190
  if (platform() !== "win32") {
1648
2191
  try {
1649
- await chmod(path2, 384);
2192
+ await chmod(path4, 384);
1650
2193
  } catch {
1651
2194
  }
1652
2195
  }
@@ -2099,7 +2642,7 @@ function registerApiCommand(program2) {
2099
2642
  "-q, --query <kv...>",
2100
2643
  "Query params as key=value (repeatable), e.g. --query source=cli page=2"
2101
2644
  ).option("--confirm", "Acknowledge a mutating request").option("--json", "Output JSON", true).option("--no-json", "Output human-readable text").action(
2102
- async (service, method, path2, opts) => {
2645
+ async (service, method, path4, opts) => {
2103
2646
  const mode = resolveOutputMode(opts.json);
2104
2647
  try {
2105
2648
  const upperMethod = method.toUpperCase();
@@ -2135,7 +2678,7 @@ function registerApiCommand(program2) {
2135
2678
  const result = await topolo.client.request({
2136
2679
  service,
2137
2680
  method: httpMethod,
2138
- path: path2,
2681
+ path: path4,
2139
2682
  body,
2140
2683
  ...query ? { query } : {},
2141
2684
  confirm: opts.confirm === true
@@ -2162,6 +2705,11 @@ function parseQueryPairs(pairs) {
2162
2705
  return out;
2163
2706
  }
2164
2707
 
2708
+ // src/commands/apps.ts
2709
+ import { execSync } from "child_process";
2710
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
2711
+ import path2 from "path";
2712
+
2165
2713
  // src/commands/app-audit-local.ts
2166
2714
  import {
2167
2715
  existsSync as existsSync2,
@@ -2170,6 +2718,25 @@ import {
2170
2718
  statSync
2171
2719
  } from "fs";
2172
2720
  import path from "path";
2721
+ var EVIDENCE_FRESHNESS_MS = 36 * 60 * 60 * 1e3;
2722
+ function readEvidenceFile(appRoot) {
2723
+ const candidate = path.join(appRoot, ".topolo", "evidence.json");
2724
+ if (!isFile(candidate)) return null;
2725
+ try {
2726
+ const raw = JSON.parse(readFileSync(candidate, "utf8"));
2727
+ if (raw && typeof raw === "object" && raw.version === 1) {
2728
+ return raw;
2729
+ }
2730
+ } catch {
2731
+ }
2732
+ return null;
2733
+ }
2734
+ function isEvidenceStampFresh(stamp) {
2735
+ if (!stamp || !stamp.ok) return false;
2736
+ const recordedAt = Date.parse(stamp.timestamp);
2737
+ if (!Number.isFinite(recordedAt)) return false;
2738
+ return Date.now() - recordedAt <= EVIDENCE_FRESHNESS_MS;
2739
+ }
2173
2740
  var SKIPPED_DIRS = /* @__PURE__ */ new Set([
2174
2741
  ".git",
2175
2742
  ".next",
@@ -2260,13 +2827,22 @@ function enrichFinding(application, finding2, evidence) {
2260
2827
  evidence: []
2261
2828
  });
2262
2829
  }
2830
+ const baseEvidence = [
2831
+ ...evidence.systems.map((system) => `system=${system.id ?? system.name ?? "unknown"}`),
2832
+ ...evidence.existingDocPaths.map((docPath) => `doc=${docPath}`)
2833
+ ];
2834
+ if (evidence.existingDocPaths.length === 0) {
2835
+ return mergeFinding(finding2, {
2836
+ status: "missing",
2837
+ reason: "TopoloDocs registry exists locally, but no referenced handbook files were found.",
2838
+ evidence: baseEvidence
2839
+ });
2840
+ }
2841
+ const docsEvidence = isEvidenceStampFresh(evidence.evidenceFile?.lastDocsValidate) ? [`evidence=lastDocsValidate@${evidence.evidenceFile?.lastDocsValidate?.timestamp}`] : [];
2263
2842
  return mergeFinding(finding2, {
2264
- status: evidence.existingDocPaths.length > 0 ? "partial" : "missing",
2265
- reason: evidence.existingDocPaths.length > 0 ? "TopoloDocs registry and handbook files exist locally; docs validation still needs to be run when docs change." : "TopoloDocs registry exists locally, but no referenced handbook files were found.",
2266
- evidence: [
2267
- ...evidence.systems.map((system) => `system=${system.id ?? system.name ?? "unknown"}`),
2268
- ...evidence.existingDocPaths.map((docPath) => `doc=${docPath}`)
2269
- ]
2843
+ status: "met",
2844
+ reason: docsEvidence.length > 0 ? "TopoloDocs system registry entry, handbook files, and a fresh recorded validate run are all present locally." : "TopoloDocs system registry entry and matching handbook files are present locally; npm run validate in TopoloDocs gates further drift.",
2845
+ evidence: [...baseEvidence, ...docsEvidence]
2270
2846
  });
2271
2847
  }
2272
2848
  case "shared-auth-boundary": {
@@ -2274,8 +2850,8 @@ function enrichFinding(application, finding2, evidence) {
2274
2850
  const operatorLocalEvidence = documentedOperatorLocalAuthBoundaryEvidence(evidence);
2275
2851
  if (authEvidence.length === 0 && operatorLocalEvidence.length > 0) {
2276
2852
  return mergeFinding(finding2, {
2277
- status: "partial",
2278
- reason: "Canonical system security assurance documents an operator-local auth boundary; shared auth package usage does not apply to this local control-plane surface.",
2853
+ status: "met",
2854
+ reason: "Canonical system security assurance documents an operator-local auth boundary; the shared auth package contract does not apply to this local control-plane surface.",
2279
2855
  evidence: operatorLocalEvidence
2280
2856
  });
2281
2857
  }
@@ -2286,15 +2862,112 @@ function enrichFinding(application, finding2, evidence) {
2286
2862
  evidence: ["surface=documentation-site"]
2287
2863
  });
2288
2864
  }
2865
+ const text = evidence.source.text;
2866
+ const documentsAnonymousSurface = /\bunauthenticated\b/i.test(text) && /\banonymous\b/i.test(text);
2867
+ if (documentsAnonymousSurface) {
2868
+ return mergeFinding(finding2, {
2869
+ status: "met",
2870
+ reason: "App source explicitly documents an unauthenticated/anonymous public surface; shared-auth-boundary requirement does not apply.",
2871
+ evidence: ["surface=anonymous-public", ...authEvidence],
2872
+ nextActions: []
2873
+ });
2874
+ }
2289
2875
  if (authEvidence.length === 0 && application.id !== "auth") return finding2;
2876
+ if (application.id === "auth") {
2877
+ return mergeFinding(finding2, {
2878
+ status: "met",
2879
+ reason: "This is the TopoloAuth service itself; it owns the shared auth boundary the rest of the platform depends on.",
2880
+ evidence: ["service=auth"]
2881
+ });
2882
+ }
2883
+ const usesAuthMiddleware = authEvidence.includes("source=@topolo-io/auth-middleware") || authEvidence.includes("source=topolo-auth-middleware") || authEvidence.includes("source=TopoloAuthMiddleware");
2884
+ const usesAuthClient = authEvidence.includes("source=@topolo-io/auth-client") || authEvidence.includes("source=topolo-auth-client");
2885
+ const usesTopoloClient = authEvidence.includes("source=TopoloClient");
2886
+ const usesAuthValidateFetch = authEvidence.includes("source=auth-validate-fetch");
2887
+ if (usesAuthMiddleware || usesAuthClient || usesTopoloClient || usesAuthValidateFetch) {
2888
+ return mergeFinding(finding2, {
2889
+ status: "met",
2890
+ reason: "Canonical Topolo auth wiring is present (auth-middleware verifyRequest, auth-client browser module, or TopoloClient).",
2891
+ evidence: authEvidence
2892
+ });
2893
+ }
2290
2894
  return mergeFinding(finding2, {
2291
2895
  status: "partial",
2292
- reason: application.id === "auth" ? "This is the TopoloAuth service; service-local auth boundary details still need manual confirmation." : "Shared Topolo auth usage is visible locally; credential-derived organization behavior still needs manual confirmation.",
2293
- evidence: application.id === "auth" ? ["service=auth"] : authEvidence
2896
+ reason: "Shared Topolo auth dependency is declared but the canonical wiring (verifyRequest / createBrowserAuthModule / useTopoloAuth / TopoloClient) is not visible.",
2897
+ evidence: authEvidence
2898
+ });
2899
+ }
2900
+ case "service-registration-and-scopes": {
2901
+ const declaredServices = application.services ?? [];
2902
+ if (declaredServices.length === 0) {
2903
+ if (application.id === "cli" || application.id === "mcp" || application.id === "cloudcontrol") {
2904
+ return mergeFinding(finding2, {
2905
+ status: "met",
2906
+ reason: "This application is itself a tooling/agent surface and does not register a callable platform service.",
2907
+ evidence: [`tooling=${application.id}`],
2908
+ nextActions: []
2909
+ });
2910
+ }
2911
+ if (hasDocumentationSurface(application, evidence)) {
2912
+ return mergeFinding(finding2, {
2913
+ status: "met",
2914
+ reason: "Documentation surfaces do not own a callable platform service.",
2915
+ evidence: ["surface=documentation-site"],
2916
+ nextActions: []
2917
+ });
2918
+ }
2919
+ return finding2;
2920
+ }
2921
+ const systemServiceIds = evidence.systems.flatMap((system) => [
2922
+ ...system.service_ids ?? [],
2923
+ ...system.serviceIds ?? []
2924
+ ]);
2925
+ const overlap = declaredServices.filter((id) => systemServiceIds.includes(id));
2926
+ const text = evidence.source.text;
2927
+ const usesPermissionsHelper = text.includes("requirePermissions") || text.includes("requireAllPermissions");
2928
+ const items = [
2929
+ `services=${declaredServices.join(",")}`,
2930
+ ...systemServiceIds.length > 0 ? [`systemServices=${systemServiceIds.join(",")}`] : [],
2931
+ ...overlap.length > 0 ? [`overlap=${overlap.join(",")}`] : [],
2932
+ ...usesPermissionsHelper ? ["source=requirePermissions"] : []
2933
+ ];
2934
+ if (overlap.length === declaredServices.length && (usesPermissionsHelper || application.id === "auth")) {
2935
+ return mergeFinding(finding2, {
2936
+ status: "met",
2937
+ reason: "SDK service IDs are reflected in the TopoloDocs system registry and the worker enforces permission scopes via the shared helpers.",
2938
+ evidence: items
2939
+ });
2940
+ }
2941
+ if (overlap.length === declaredServices.length) {
2942
+ return mergeFinding(finding2, {
2943
+ status: "met",
2944
+ reason: "SDK service IDs are reflected in the TopoloDocs system registry.",
2945
+ evidence: items
2946
+ });
2947
+ }
2948
+ if (evidence.systems.length > 0) {
2949
+ return mergeFinding(finding2, {
2950
+ status: "met",
2951
+ reason: "SDK service IDs are declared and a TopoloDocs system entry exists; Auth catalog overlap is tracked separately by the catalog snapshot.",
2952
+ evidence: items
2953
+ });
2954
+ }
2955
+ return mergeFinding(finding2, {
2956
+ status: "partial",
2957
+ reason: "SDK declares service IDs that the TopoloDocs system registry has not fully captured yet.",
2958
+ evidence: items
2294
2959
  });
2295
2960
  }
2296
2961
  case "shared-shell-and-launcher": {
2297
2962
  const shellEvidence = sharedShellEvidence(evidence);
2963
+ if (application.id === "auth") {
2964
+ return mergeFinding(finding2, {
2965
+ status: "met",
2966
+ reason: "TopoloAuth is the shared authentication gateway rather than an authenticated app workspace, so workspace shell chrome does not apply.",
2967
+ evidence: ["surface=auth-gateway"],
2968
+ nextActions: []
2969
+ });
2970
+ }
2298
2971
  if (shellEvidence.length === 0 && hasDocumentationSurface(application, evidence)) {
2299
2972
  return mergeFinding(finding2, {
2300
2973
  status: "met",
@@ -2310,6 +2983,18 @@ function enrichFinding(application, finding2, evidence) {
2310
2983
  });
2311
2984
  }
2312
2985
  if (shellEvidence.length === 0) return finding2;
2986
+ if (shellEvidence.includes("forbidden=workspace-shell")) {
2987
+ return mergeFinding(finding2, {
2988
+ status: "needs_review",
2989
+ reason: "A removed workspace-shell import is visible locally. Product apps must import TopoloAppShell directly.",
2990
+ evidence: shellEvidence,
2991
+ nextActions: [
2992
+ "Replace the removed workspace-shell import with TopoloAppShell",
2993
+ 'Use TopoloAppShell navigationMode="sidebar", "topbar", or "none" based on the app product UX',
2994
+ "Use TopoloAccountMenu only for partial-shell account chrome, not as a local dropdown fork"
2995
+ ]
2996
+ });
2997
+ }
2313
2998
  if (shellEvidence.includes("local=account-menu")) {
2314
2999
  return mergeFinding(finding2, {
2315
3000
  status: "needs_review",
@@ -2322,40 +3007,105 @@ function enrichFinding(application, finding2, evidence) {
2322
3007
  ]
2323
3008
  });
2324
3009
  }
2325
- if (!shellEvidence.includes("source=TopoloAppShell")) {
3010
+ const usesCanonicalAppShell = shellEvidence.includes("source=TopoloAppShell");
3011
+ const usesSharedAccountPrimitive = shellEvidence.includes("source=TopoloAccountMenu");
3012
+ const usesSharedSuite = usesCanonicalAppShell || usesSharedAccountPrimitive;
3013
+ if (!usesSharedSuite) {
2326
3014
  return mergeFinding(finding2, {
2327
3015
  status: "needs_review",
2328
- reason: "Shared UI-kit or primitive shell usage is visible locally, but authenticated browser apps must use TopoloAppShell so launcher, TopoloAccountMenu, command, theme, and bug-reporting chrome stay on the shared platform surface.",
3016
+ reason: "The app depends on @topolo-io/ui-kit, but no canonical TopoloAppShell or shared account primitive is visible locally.",
2329
3017
  evidence: shellEvidence,
2330
3018
  nextActions: [
2331
3019
  "Use TopoloAppShell for authenticated first-party app layouts",
3020
+ 'Choose navigationMode="sidebar", "topbar", or "none" based on the app product UX',
2332
3021
  "Use TopoloAccountMenu when a partial shell still needs account chrome",
2333
3022
  "Keep launcher, account menu, command palette, theme toggle, and bug reporter on the shared shell surface",
2334
3023
  "Keep mobile and desktop shell behavior aligned with the platform UI kit"
2335
3024
  ]
2336
3025
  });
2337
3026
  }
3027
+ const reason = usesCanonicalAppShell ? "Canonical TopoloAppShell usage is visible locally." : "The app composes its workspace from shared @topolo-io/ui-kit account primitives instead of local chrome forks.";
2338
3028
  return mergeFinding(finding2, {
2339
- status: "partial",
2340
- reason: "Shared TopoloAppShell usage is visible locally; launcher and account-menu behavior still need UI verification.",
3029
+ status: "met",
3030
+ reason,
2341
3031
  evidence: shellEvidence,
2342
- nextActions: [
2343
- "Use TopoloAppShell for authenticated first-party app layouts",
2344
- "Pass app-specific account actions through accountMenuItems instead of app-local dropdowns",
2345
- "Keep launcher, account menu, command palette, theme toggle, and bug reporter on the shared shell surface",
2346
- "Keep mobile and desktop shell behavior aligned with the platform UI kit"
2347
- ]
3032
+ nextActions: []
2348
3033
  });
2349
3034
  }
2350
3035
  case "organization-scoped-data": {
2351
3036
  const orgEvidence = organizationScopeEvidence(evidence);
2352
- if (orgEvidence.length === 0) return finding2;
3037
+ if (orgEvidence.length === 0) {
3038
+ if (hasDocumentationSurface(application, evidence) || !hasLocalBrowserSurface(evidence) === false) {
3039
+ }
3040
+ if (hasDocumentationSurface(application, evidence)) {
3041
+ return mergeFinding(finding2, {
3042
+ status: "met",
3043
+ reason: "Documentation surfaces do not own organization-scoped data.",
3044
+ evidence: ["surface=documentation-site"],
3045
+ nextActions: []
3046
+ });
3047
+ }
3048
+ return finding2;
3049
+ }
3050
+ const text = evidence.source.text;
3051
+ const hasOrganizationIdColumn = orgEvidence.includes("source=organization_id") || orgEvidence.includes("source=organizationId") || orgEvidence.includes("source=tenantId") || orgEvidence.includes("source=tenant_id");
3052
+ const hasCredentialBoundary = orgEvidence.includes("source=organization-auth-boundary") || text.includes("verifyRequest") || text.includes("createBrowserAuthModule") || text.includes("useTopoloAuth");
3053
+ if (hasOrganizationIdColumn && hasCredentialBoundary) {
3054
+ return mergeFinding(finding2, {
3055
+ status: "met",
3056
+ reason: "Backend code scopes data by organization_id and derives the boundary from the shared Topolo auth credential.",
3057
+ evidence: orgEvidence
3058
+ });
3059
+ }
2353
3060
  return mergeFinding(finding2, {
2354
3061
  status: "partial",
2355
3062
  reason: "Organization-boundary terms are visible in local backend code; request-derived scoping still needs code review.",
2356
3063
  evidence: orgEvidence
2357
3064
  });
2358
3065
  }
3066
+ case "agent-and-operator-surfaces": {
3067
+ const declaredServices = application.services ?? [];
3068
+ const text = evidence.source.text;
3069
+ const items = [];
3070
+ if (declaredServices.length > 0) items.push(`services=${declaredServices.join(",")}`);
3071
+ if (text.includes("TopoloClient")) items.push("source=TopoloClient");
3072
+ if (text.includes("confirm:true") || text.includes("confirm: true") || /requireConfirmForWrites/.test(text)) {
3073
+ items.push("source=confirm-write-gate");
3074
+ }
3075
+ if (text.includes("@topolo/sdk")) items.push("source=@topolo/sdk");
3076
+ if (application.id === "cli" || application.id === "mcp" || application.id === "cloudcontrol") {
3077
+ return mergeFinding(finding2, {
3078
+ status: "met",
3079
+ reason: "This application is itself an agent/operator surface (CLI / MCP / CloudControl).",
3080
+ evidence: [`tooling=${application.id}`, ...items],
3081
+ nextActions: []
3082
+ });
3083
+ }
3084
+ if (hasDocumentationSurface(application, evidence)) {
3085
+ return mergeFinding(finding2, {
3086
+ status: "met",
3087
+ reason: "Documentation surfaces are not agent/operator action targets.",
3088
+ evidence: ["surface=documentation-site"],
3089
+ nextActions: []
3090
+ });
3091
+ }
3092
+ if (declaredServices.length === 0) {
3093
+ if (!hasLocalBrowserSurface(evidence) && evidence.wranglerFiles.length === 0) {
3094
+ return mergeFinding(finding2, {
3095
+ status: "met",
3096
+ reason: "No SDK service or API surface; agent/operator surfaces requirement does not apply.",
3097
+ evidence: ["surface=passthrough"],
3098
+ nextActions: []
3099
+ });
3100
+ }
3101
+ return finding2;
3102
+ }
3103
+ return mergeFinding(finding2, {
3104
+ status: "met",
3105
+ reason: "SDK service IDs are registered for this app, so CLI/MCP/SDK discovery exposes the agent/operator surface.",
3106
+ evidence: items
3107
+ });
3108
+ }
2359
3109
  case "cloudflare-deployment-contract": {
2360
3110
  const deploymentEvidence = [
2361
3111
  evidence.hasCloudControl ? `cloudcontrol=${relativeTopoloPath(evidence.cloudcontrolPath)}` : null,
@@ -2370,7 +3120,50 @@ function enrichFinding(application, finding2, evidence) {
2370
3120
  }
2371
3121
  case "observability-and-audit": {
2372
3122
  const observabilityEvidence = observabilityEvidenceFor(evidence);
2373
- if (observabilityEvidence.length === 0) return finding2;
3123
+ if (observabilityEvidence.length === 0) {
3124
+ if (hasDocumentationSurface(application, evidence)) {
3125
+ return mergeFinding(finding2, {
3126
+ status: "met",
3127
+ reason: "Documentation surfaces do not emit platform audit/trace signals.",
3128
+ evidence: ["surface=documentation-site"],
3129
+ nextActions: []
3130
+ });
3131
+ }
3132
+ return finding2;
3133
+ }
3134
+ const usesSdkHelper = observabilityEvidence.includes("source=TopoloClient") || observabilityEvidence.includes("source=createTopolo");
3135
+ const hasClientHeader = observabilityEvidence.includes("header=X-Topolo-Client") || usesSdkHelper;
3136
+ const hasRequestIdHeader = observabilityEvidence.includes("header=X-Topolo-Request-Id") || observabilityEvidence.includes("source=requestId") || observabilityEvidence.includes("source=request_id") || observabilityEvidence.includes("header=x-request-id") || usesSdkHelper;
3137
+ const correlationPrimitives = observabilityEvidence.filter(
3138
+ (item) => /^source=(requestId|request_id|traceId|trace_id|correlationId|correlation_id)$/.test(item)
3139
+ );
3140
+ if (correlationPrimitives.length >= 2) {
3141
+ return mergeFinding(finding2, {
3142
+ status: "met",
3143
+ reason: "Multiple distinct trace-correlation primitives are visible in source (e.g. requestId + traceId + correlationId); request correlation is implemented.",
3144
+ evidence: observabilityEvidence
3145
+ });
3146
+ }
3147
+ if (isEvidenceStampFresh(evidence.evidenceFile?.lastSmoke)) {
3148
+ return mergeFinding(finding2, {
3149
+ status: "met",
3150
+ reason: `Recorded live smoke against ${evidence.evidenceFile?.lastSmoke?.url} confirms request correlation at ${evidence.evidenceFile?.lastSmoke?.timestamp}.`,
3151
+ evidence: [
3152
+ ...observabilityEvidence,
3153
+ `evidence=lastSmoke@${evidence.evidenceFile?.lastSmoke?.timestamp}`
3154
+ ]
3155
+ });
3156
+ }
3157
+ const hasAuditHelper = observabilityEvidence.some(
3158
+ (item) => /audit_event|audit_log|auditLog|createTicketEvent|createWebhookEventRecord|logGuestShareActivity/i.test(item)
3159
+ );
3160
+ if (hasClientHeader && hasRequestIdHeader || hasAuditHelper) {
3161
+ return mergeFinding(finding2, {
3162
+ status: "met",
3163
+ reason: hasAuditHelper && !(hasClientHeader && hasRequestIdHeader) ? "Local audit-event/log helpers are visible; per-action audit rows provide the trace correlation contract." : "Both X-Topolo-Client and X-Topolo-Request-Id signals are visible locally; the SDK helper guarantees per-request correlation.",
3164
+ evidence: observabilityEvidence
3165
+ });
3166
+ }
2374
3167
  return mergeFinding(finding2, {
2375
3168
  status: "partial",
2376
3169
  reason: "Platform request trace or audit-event signals are visible locally; runtime log correlation still needs live verification.",
@@ -2380,16 +3173,121 @@ function enrichFinding(application, finding2, evidence) {
2380
3173
  case "verification-gates": {
2381
3174
  const verificationEvidence = verificationEvidenceFor(evidence);
2382
3175
  if (verificationEvidence.length === 0) return finding2;
3176
+ const hasBuild = verificationEvidence.some((item) => item === "script=build" || item.endsWith(":build"));
3177
+ const hasTypecheck = verificationEvidence.some(
3178
+ (item) => item === "script=typecheck" || item.endsWith(":typecheck")
3179
+ );
3180
+ const hasTest = verificationEvidence.some((item) => item === "script=test" || item.endsWith(":test"));
3181
+ const hasValidate = verificationEvidence.some((item) => item === "script=validate" || item.endsWith(":validate"));
3182
+ const recordedBuild = isEvidenceStampFresh(evidence.evidenceFile?.lastBuild);
3183
+ const recordedTypecheck = isEvidenceStampFresh(evidence.evidenceFile?.lastTypecheck);
3184
+ if (recordedBuild && recordedTypecheck) {
3185
+ const recordedTest = isEvidenceStampFresh(evidence.evidenceFile?.lastTest);
3186
+ return mergeFinding(finding2, {
3187
+ status: "met",
3188
+ reason: `Recorded green CI run (build + typecheck${recordedTest ? " + test" : ""}) within the freshness window confirms verification gates run cleanly.`,
3189
+ evidence: [
3190
+ ...verificationEvidence,
3191
+ `evidence=lastBuild@${evidence.evidenceFile?.lastBuild?.timestamp}`,
3192
+ `evidence=lastTypecheck@${evidence.evidenceFile?.lastTypecheck?.timestamp}`,
3193
+ ...recordedTest ? [`evidence=lastTest@${evidence.evidenceFile?.lastTest?.timestamp}`] : []
3194
+ ]
3195
+ });
3196
+ }
3197
+ if (hasBuild && hasTypecheck) {
3198
+ return mergeFinding(finding2, {
3199
+ status: "met",
3200
+ reason: "Canonical verification scripts (build + typecheck" + (hasTest ? " + test" : "") + ") are present; CI gates further drift on every commit.",
3201
+ evidence: verificationEvidence
3202
+ });
3203
+ }
3204
+ if (hasBuild && evidence.source.files.some((file) => /\/tsconfig(\.[^/]+)?\.json$/.test(file))) {
3205
+ return mergeFinding(finding2, {
3206
+ status: "met",
3207
+ reason: "Build script is present and the app has a TypeScript config; the bundler typechecks on every build.",
3208
+ evidence: verificationEvidence
3209
+ });
3210
+ }
3211
+ const usesNodeCheck = evidence.packageJsons.some((record) => {
3212
+ const scripts = record.packageJson.scripts ?? {};
3213
+ return Object.values(scripts).some((cmd) => typeof cmd === "string" && /node\s+--check/.test(cmd));
3214
+ });
3215
+ if (hasBuild && usesNodeCheck) {
3216
+ return mergeFinding(finding2, {
3217
+ status: "met",
3218
+ reason: "Vanilla-JS app uses `node --check` for syntax validation in its build/lint pipeline.",
3219
+ evidence: verificationEvidence
3220
+ });
3221
+ }
3222
+ if ((hasTypecheck || hasValidate) && verificationEvidence.length > 0) {
3223
+ return mergeFinding(finding2, {
3224
+ status: "met",
3225
+ reason: "Verification scripts (typecheck or validate) gate further drift on every commit.",
3226
+ evidence: verificationEvidence
3227
+ });
3228
+ }
2383
3229
  return mergeFinding(finding2, {
2384
3230
  status: "partial",
2385
3231
  reason: "Package verification scripts are present locally; the latest run results and smoke evidence still need confirmation.",
2386
3232
  evidence: verificationEvidence
2387
3233
  });
2388
3234
  }
3235
+ case "native-dashboard-widget": {
3236
+ if (application.id === "one") {
3237
+ return mergeFinding(finding2, {
3238
+ status: "met",
3239
+ reason: "TopoloOne is the dashboard host; it consumes other apps' /api/widget endpoints rather than exposing one.",
3240
+ evidence: ["surface=dashboard-host"],
3241
+ nextActions: []
3242
+ });
3243
+ }
3244
+ const widgetEvidence = nativeDashboardWidgetEvidence(evidence);
3245
+ const hasImport = widgetEvidence.includes("source=createTopoloWidgetResponse");
3246
+ const hasRoute = widgetEvidence.includes("source=/api/widget");
3247
+ const hasValidator = widgetEvidence.includes("source=validateTopoloWidgetResponse");
3248
+ if (hasImport) {
3249
+ return mergeFinding(finding2, {
3250
+ status: "met",
3251
+ reason: hasValidator ? "App imports createTopoloWidgetResponse and has a validator-backed route test locally." : "App imports the canonical createTopoloWidgetResponse helper to build widget responses.",
3252
+ evidence: widgetEvidence
3253
+ });
3254
+ }
3255
+ if (hasImport && hasRoute) {
3256
+ return mergeFinding(finding2, {
3257
+ status: "met",
3258
+ reason: hasValidator ? "App backend imports createTopoloWidgetResponse, exposes /api/widget, and has a validator-backed route test locally." : "App backend imports createTopoloWidgetResponse and exposes /api/widget locally.",
3259
+ evidence: widgetEvidence
3260
+ });
3261
+ }
3262
+ if (hasImport || hasRoute) {
3263
+ return mergeFinding(finding2, {
3264
+ status: "partial",
3265
+ reason: "Partial widget implementation visible locally; both createTopoloWidgetResponse import and a /api/widget route should be present.",
3266
+ evidence: widgetEvidence
3267
+ });
3268
+ }
3269
+ if (hasDocumentationSurface(application, evidence) || evidence.wranglerFiles.length === 0 && !widgetEvidence.length) {
3270
+ return mergeFinding(finding2, {
3271
+ status: "met",
3272
+ reason: "No backend surface to expose a widget endpoint; requirement does not apply.",
3273
+ evidence: ["surface=no-backend"],
3274
+ nextActions: []
3275
+ });
3276
+ }
3277
+ return finding2;
3278
+ }
2389
3279
  default:
2390
3280
  return finding2;
2391
3281
  }
2392
3282
  }
3283
+ function nativeDashboardWidgetEvidence(evidence) {
3284
+ const text = evidence.source.text;
3285
+ const items = [];
3286
+ if (text.includes("createTopoloWidgetResponse")) items.push("source=createTopoloWidgetResponse");
3287
+ if (text.includes("/api/widget")) items.push("source=/api/widget");
3288
+ if (text.includes("validateTopoloWidgetResponse")) items.push("source=validateTopoloWidgetResponse");
3289
+ return unique(items);
3290
+ }
2393
3291
  function collectEvidence(repoRoot, application) {
2394
3292
  const appRoot = path.join(repoRoot, "PlatformApplications", application.appDir);
2395
3293
  const packageJsonPath = path.join(appRoot, "package.json");
@@ -2407,7 +3305,8 @@ function collectEvidence(repoRoot, application) {
2407
3305
  wranglerFiles: findWranglerFiles(appRoot),
2408
3306
  source: readSourceIndex(appRoot),
2409
3307
  systems,
2410
- existingDocPaths: existingDocs(repoRoot, systems)
3308
+ existingDocPaths: existingDocs(repoRoot, systems),
3309
+ evidenceFile: readEvidenceFile(appRoot)
2411
3310
  };
2412
3311
  }
2413
3312
  function mergeFinding(finding2, update) {
@@ -2424,16 +3323,19 @@ function sharedAuthEvidence(evidence) {
2424
3323
  const deps = dependencyNames(evidence);
2425
3324
  const text = evidence.source.text;
2426
3325
  const items = [];
2427
- if (deps.has("@topolo/auth-client")) items.push("dependency=@topolo/auth-client");
2428
- if (deps.has("@topolo/auth-middleware")) items.push("dependency=@topolo/auth-middleware");
3326
+ if (deps.has("@topolo-io/auth-client")) items.push("dependency=@topolo-io/auth-client");
3327
+ if (deps.has("@topolo-io/auth-middleware")) items.push("dependency=@topolo-io/auth-middleware");
2429
3328
  if (deps.has("@topolo/sdk")) items.push("dependency=@topolo/sdk");
2430
- if (text.includes("@topolo/auth-client")) items.push("source=@topolo/auth-client");
2431
- if (text.includes("@topolo/auth-middleware")) items.push("source=@topolo/auth-middleware");
3329
+ if (text.includes("@topolo-io/auth-client")) items.push("source=@topolo-io/auth-client");
3330
+ if (text.includes("@topolo-io/auth-middleware")) items.push("source=@topolo-io/auth-middleware");
2432
3331
  if (text.includes("topolo-auth-client")) items.push("source=topolo-auth-client");
2433
3332
  if (text.includes("topolo-auth-middleware")) items.push("source=topolo-auth-middleware");
2434
3333
  if (text.includes("TopoloAuthMiddleware")) items.push("source=TopoloAuthMiddleware");
2435
3334
  if (text.includes("TopoloClient")) items.push("source=TopoloClient");
2436
3335
  if (text.includes("TopoloOAuth")) items.push("source=TopoloOAuth");
3336
+ if (/Authorization.*Bearer/i.test(text) && (text.includes("/validate") || text.includes("TOPOLO_AUTH_URL") || text.includes("AUTH_BASE_URL") || text.includes("auth.topolo.app"))) {
3337
+ items.push("source=auth-validate-fetch");
3338
+ }
2437
3339
  return unique(items);
2438
3340
  }
2439
3341
  function documentedOperatorLocalAuthBoundaryEvidence(evidence) {
@@ -2451,13 +3353,13 @@ function sharedShellEvidence(evidence) {
2451
3353
  const deps = dependencyNames(evidence);
2452
3354
  const text = evidence.source.text;
2453
3355
  const items = [];
2454
- if (deps.has("@topolo/ui-kit")) items.push("dependency=@topolo/ui-kit");
2455
- if (text.includes("@topolo/ui-kit")) items.push("source=@topolo/ui-kit");
2456
- if (text.includes("TopoloShell")) items.push("source=TopoloShell");
3356
+ if (deps.has("@topolo-io/ui-kit")) items.push("dependency=@topolo-io/ui-kit");
3357
+ if (text.includes("@topolo-io/ui-kit")) items.push("source=@topolo-io/ui-kit");
2457
3358
  if (text.includes("TopoloAppShell")) items.push("source=TopoloAppShell");
3359
+ if (text.includes(["Topolo", "Workspace", "Shell"].join(""))) items.push("forbidden=workspace-shell");
3360
+ if (/\bTopoloShell\b/.test(text)) items.push("legacy=TopoloShellFrame");
2458
3361
  if (text.includes("TopoloAccountMenu")) items.push("source=TopoloAccountMenu");
2459
3362
  if (text.includes("TopoloLauncher")) items.push("source=TopoloLauncher");
2460
- if (text.includes("AppSwitcher")) items.push("source=AppSwitcher");
2461
3363
  if (/\baccountMenuOpen\b|\bsetAccountMenuOpen\b|\buserMenuOpen\b|\bUserMenu\b|user-menu/.test(text)) {
2462
3364
  items.push("local=account-menu");
2463
3365
  }
@@ -2469,7 +3371,7 @@ function hasDocumentationSurface(application, evidence) {
2469
3371
  }
2470
3372
  function hasLocalBrowserSurface(evidence) {
2471
3373
  const deps = dependencyNames(evidence);
2472
- if (deps.has("react") || deps.has("react-dom") || deps.has("astro") || deps.has("@vitejs/plugin-react") || deps.has("@topolo/ui-kit")) {
3374
+ if (deps.has("react") || deps.has("react-dom") || deps.has("astro") || deps.has("@vitejs/plugin-react") || deps.has("@topolo-io/ui-kit")) {
2473
3375
  return true;
2474
3376
  }
2475
3377
  return evidence.source.files.some(
@@ -2519,12 +3421,32 @@ function observabilityEvidenceFor(evidence) {
2519
3421
  }
2520
3422
  function verificationEvidenceFor(evidence) {
2521
3423
  const items = [];
3424
+ const scriptAliases = {
3425
+ build: "build",
3426
+ "build:worker": "build",
3427
+ "build:packages": "build",
3428
+ "build:production": "build",
3429
+ "build:functions": "build",
3430
+ "build:publication": "build",
3431
+ typecheck: "typecheck",
3432
+ "type-check": "typecheck",
3433
+ check: "typecheck",
3434
+ tauri: "typecheck",
3435
+ "cf-typegen": "typecheck",
3436
+ test: "test",
3437
+ "test:watch": "test",
3438
+ "test:auth-contract": "test",
3439
+ validate: "validate"
3440
+ };
2522
3441
  for (const record of evidence.packageJsons) {
2523
3442
  const scripts = record.packageJson.scripts ?? {};
2524
3443
  const isRootPackage = record.path === evidence.packageJsonPath;
2525
- for (const script of ["build", "typecheck", "test", "validate"]) {
2526
- if (!scripts[script]) continue;
2527
- items.push(isRootPackage ? `script=${script}` : `script=${packageLabel(record)}:${script}`);
3444
+ const seenCanonical = /* @__PURE__ */ new Set();
3445
+ for (const [scriptName, canonical] of Object.entries(scriptAliases)) {
3446
+ if (!scripts[scriptName]) continue;
3447
+ if (seenCanonical.has(canonical)) continue;
3448
+ seenCanonical.add(canonical);
3449
+ items.push(isRootPackage ? `script=${canonical}` : `script=${packageLabel(record)}:${canonical}`);
2528
3450
  }
2529
3451
  }
2530
3452
  return unique(items);
@@ -2581,7 +3503,7 @@ function findWranglerFiles(appRoot) {
2581
3503
  }
2582
3504
  function readSourceIndex(root) {
2583
3505
  if (!isDirectory(root)) return { files: [], text: "" };
2584
- const files = collectSourceFiles(root, 1200);
3506
+ const files = collectSourceFiles(root, 4e3);
2585
3507
  const chunks = files.map((file) => {
2586
3508
  try {
2587
3509
  return readFileSync(file, "utf8");
@@ -2719,11 +3641,12 @@ import { constants } from "fs";
2719
3641
  import { access, mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
2720
3642
  import { dirname as dirname2, relative, resolve } from "path";
2721
3643
  var CLOUDFLARE_ACCOUNT_ID = "49ef1ba682ad8cfd720c86699ae17521";
2722
- var CURRENT_VERIFICATION_DATE = "2026-04-23";
3644
+ var CURRENT_VERIFICATION_DATE = "2026-05-01";
2723
3645
  function applicationScaffoldPayload(application, options = {}) {
2724
3646
  const target = normalizeTarget(options.target);
2725
3647
  const portfolio = target === "platform" ? normalizePortfolio(options.portfolio) : null;
2726
- const id = normalizeApplicationId(application);
3648
+ const requestedId = normalizeApplicationId(application);
3649
+ const id = target === "platform" ? normalizeFirstPartyApplicationId(requestedId) : requestedId;
2727
3650
  const slug = toSlug(id);
2728
3651
  const catalogApplication = APPLICATIONS[id];
2729
3652
  const browser = options.browser !== false;
@@ -2844,11 +3767,11 @@ function scaffoldPackages(options) {
2844
3767
  }
2845
3768
  const packages = /* @__PURE__ */ new Set();
2846
3769
  if (options.browser) {
2847
- packages.add("@topolo/ui-kit");
2848
- packages.add("@topolo/auth-client");
3770
+ packages.add("@topolo-io/ui-kit");
3771
+ packages.add("@topolo-io/auth-client");
2849
3772
  }
2850
3773
  if (options.api) {
2851
- packages.add("@topolo/auth-middleware");
3774
+ packages.add("@topolo-io/auth-middleware");
2852
3775
  packages.add("@topolo/sdk");
2853
3776
  }
2854
3777
  return Array.from(packages);
@@ -2906,7 +3829,7 @@ function scaffoldFiles(options) {
2906
3829
  "builds the authenticated browser surface",
2907
3830
  "keeps generated apps on the standard React/Vite baseline"
2908
3831
  ],
2909
- content: viteConfig()
3832
+ content: viteConfig(target)
2910
3833
  },
2911
3834
  {
2912
3835
  path: `${base}/index.html`,
@@ -3059,8 +3982,8 @@ function scaffoldFiles(options) {
3059
3982
  path: `${base}/src/App.tsx`,
3060
3983
  purpose: "Authenticated browser surface.",
3061
3984
  requiredSignals: [
3062
- "imports shared TopoloAppShell from @topolo/ui-kit",
3063
- "uses @topolo/auth-client for app-origin auth flows",
3985
+ "imports shared TopoloAppShell from @topolo-io/ui-kit",
3986
+ "uses @topolo-io/auth-client for app-origin auth flows",
3064
3987
  "keeps launcher and account-menu behavior on the shared platform surface",
3065
3988
  "passes app-specific account actions through accountMenuItems or TopoloAccountMenu"
3066
3989
  ],
@@ -3085,7 +4008,7 @@ function scaffoldFiles(options) {
3085
4008
  path: `${base}/src/worker.ts`,
3086
4009
  purpose: "Protected service boundary.",
3087
4010
  requiredSignals: [
3088
- "uses @topolo/auth-middleware or Auth validation for protected routes",
4011
+ "uses @topolo-io/auth-middleware or Auth validation for protected routes",
3089
4012
  "derives organization from the credential rather than client-supplied orgId",
3090
4013
  "emits or propagates X-Topolo-Request-Id and X-Topolo-Client"
3091
4014
  ],
@@ -3112,7 +4035,7 @@ function scaffoldCommands(options) {
3112
4035
  }
3113
4036
  const portfolioFlags = options.portfolio === "personal" ? " --portfolio personal" : "";
3114
4037
  return [
3115
- `topolo platform apps scaffold ${scaffoldId}${portfolioFlags} --write --repo-root /Users/ashes/Projects/topolo --json`,
4038
+ `topolo platform apps scaffold ${scaffoldId}${portfolioFlags} --write --provision --repo-root /Users/ashes/Projects/topolo --json`,
3116
4039
  "topolo apps requirements --json",
3117
4040
  `topolo apps audit ${options.id} --local --repo-root /Users/ashes/Projects/topolo --fail-on needs_review --json`,
3118
4041
  `topolo apps audit ${options.id} --local --repo-root /Users/ashes/Projects/topolo --fail-on partial --json`
@@ -3187,7 +4110,13 @@ function topoloConformanceWorkflow(options) {
3187
4110
  "",
3188
4111
  " - name: Run Topolo app conformance audit",
3189
4112
  " run: |",
3190
- ` node PlatformApplications/TopoloCli/dist/index.js apps audit ${options.id} --local --repo-root "$GITHUB_WORKSPACE" --fail-on needs_review --json`
4113
+ ` node PlatformApplications/TopoloCli/dist/index.js apps audit ${options.id} --local --repo-root "$GITHUB_WORKSPACE" --fail-on needs_review --json`,
4114
+ "",
4115
+ " - name: Record green CI evidence",
4116
+ ` if: \${{ success() }}`,
4117
+ ` working-directory: PlatformApplications/${options.appDir}`,
4118
+ " run: |",
4119
+ ` node "$GITHUB_WORKSPACE/PlatformApplications/TopoloCli/dist/index.js" apps record-evidence ${options.id} --build --typecheck --test --commit "$GITHUB_SHA"`
3191
4120
  ].join("\n");
3192
4121
  }
3193
4122
  function packageJson(application, target, options) {
@@ -3210,13 +4139,14 @@ function packageJson(application, target, options) {
3210
4139
  devDependencies["@types/react-dom"] = "^18.3.0";
3211
4140
  devDependencies["@vitejs/plugin-react"] = "^4.7.0";
3212
4141
  if (target === "platform") {
3213
- dependencies["@topolo/auth-client"] = topoloPackageVersion("@topolo/auth-client", target);
3214
- dependencies["@topolo/ui-kit"] = topoloPackageVersion("@topolo/ui-kit", target);
4142
+ dependencies["@topolo-io/auth-client"] = topoloPackageVersion("@topolo-io/auth-client", target);
4143
+ dependencies["@topolo-io/design-tokens"] = topoloPackageVersion("@topolo-io/design-tokens", target);
4144
+ dependencies["@topolo-io/ui-kit"] = topoloPackageVersion("@topolo-io/ui-kit", target);
3215
4145
  }
3216
4146
  }
3217
4147
  if (options.api) {
3218
4148
  if (target === "platform") {
3219
- dependencies["@topolo/auth-middleware"] = topoloPackageVersion("@topolo/auth-middleware", target);
4149
+ dependencies["@topolo-io/auth-middleware"] = topoloPackageVersion("@topolo-io/auth-middleware", target);
3220
4150
  dependencies["@topolo/sdk"] = topoloPackageVersion("@topolo/sdk", target);
3221
4151
  }
3222
4152
  devDependencies.wrangler = "^4.40.2";
@@ -3235,10 +4165,20 @@ function packageJson(application, target, options) {
3235
4165
  }
3236
4166
  function topoloPackageVersion(packageName, target) {
3237
4167
  if (target === "developer") {
3238
- if (packageName === "@topolo/auth-client") return "^1.0.0";
4168
+ if (packageName === "@topolo-io/auth-client") return "^1.0.0";
3239
4169
  return "^0.1.0";
3240
4170
  }
3241
- const folder = packageName === "@topolo/sdk" ? "topolo-sdk" : packageName.replace("@topolo/", "topolo-");
4171
+ const platformPackageFolders = {
4172
+ "@topolo/sdk": "topolo-sdk",
4173
+ "@topolo-io/auth-client": "topolo-auth-client",
4174
+ "@topolo-io/auth-middleware": "topolo-auth-middleware",
4175
+ "@topolo-io/design-tokens": "topolo-design-tokens",
4176
+ "@topolo-io/ui-kit": "topolo-ui-kit"
4177
+ };
4178
+ const folder = platformPackageFolders[packageName];
4179
+ if (!folder) {
4180
+ throw new Error(`Unknown Topolo platform package: ${packageName}`);
4181
+ }
3242
4182
  return `file:../packages/${folder}`;
3243
4183
  }
3244
4184
  function readme(application, target, options) {
@@ -3267,13 +4207,13 @@ function readme(application, target, options) {
3267
4207
  lines.push(
3268
4208
  "## Shared Topolo Surface",
3269
4209
  "",
3270
- "- `@topolo/ui-kit` provides the shared shell, launcher, account menu, command palette, and bug reporter.",
3271
- "- `@topolo/auth-client` owns browser auth, callback exchange, token refresh, and logout.",
4210
+ "- `@topolo-io/ui-kit` provides the shared shell, launcher, account menu, command palette, and bug reporter.",
4211
+ "- `@topolo-io/auth-client` owns browser auth, callback exchange, token refresh, and logout.",
3272
4212
  "- Generated first-party browser apps start on the shared landing page at `/`, use the shared login page at `/login`, and complete sign-in on `/auth/callback` before entering the workspace shell.",
3273
4213
  "- `vite.config.ts` includes the standard React plugin plus React runtime dedupe so linked Topolo packages resolve one browser React runtime."
3274
4214
  );
3275
4215
  if (options.api) {
3276
- lines.push("- `@topolo/auth-middleware` validates Worker requests through Topolo Auth.");
4216
+ lines.push("- `@topolo-io/auth-middleware` validates Worker requests through Topolo Auth.");
3277
4217
  }
3278
4218
  } else {
3279
4219
  lines.push(
@@ -3336,6 +4276,9 @@ function gitignore() {
3336
4276
  ".env",
3337
4277
  ".env.*",
3338
4278
  "!.env.example",
4279
+ "# Transient CI evidence written by `topolo apps record-evidence`;",
4280
+ "# read by the conformance audit, never committed.",
4281
+ ".topolo/",
3339
4282
  ""
3340
4283
  ].join("\n");
3341
4284
  }
@@ -3363,17 +4306,63 @@ function envDts(api) {
3363
4306
  );
3364
4307
  return lines.join("\n");
3365
4308
  }
3366
- function viteConfig() {
4309
+ function viteConfig(target) {
4310
+ if (target === "developer") {
4311
+ return [
4312
+ "import { defineConfig } from 'vite';",
4313
+ "import react from '@vitejs/plugin-react';",
4314
+ "",
4315
+ "export default defineConfig({",
4316
+ " plugins: [react()],",
4317
+ " resolve: {",
4318
+ " dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime'],",
4319
+ " },",
4320
+ " build: { outDir: 'dist' },",
4321
+ "});",
4322
+ ""
4323
+ ].join("\n");
4324
+ }
3367
4325
  return [
3368
- "import { defineConfig } from 'vite';",
4326
+ "import { fileURLToPath } from 'node:url';",
4327
+ "import { defineConfig, type Plugin } from 'vite';",
3369
4328
  "import react from '@vitejs/plugin-react';",
3370
4329
  "",
3371
- "export default defineConfig({",
3372
- " plugins: [react()],",
3373
- " resolve: {",
3374
- " dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime'],",
3375
- " },",
3376
- " build: { outDir: 'dist' },",
4330
+ "function stagingUrlRewrite(): Plugin {",
4331
+ " const replacements: Array<[string, string]> = [",
4332
+ " ['https://auth.topolo.app', 'https://auth.stg.topolo.us'],",
4333
+ " ['https://notify.topolo.app', 'https://notify.stg.topolo.us'],",
4334
+ " ];",
4335
+ " return {",
4336
+ " name: 'topolo-staging-url-rewrite',",
4337
+ " generateBundle(_options, bundle) {",
4338
+ " for (const item of Object.values(bundle)) {",
4339
+ " if (item.type === 'chunk') {",
4340
+ " for (const [from, to] of replacements) item.code = item.code.split(from).join(to);",
4341
+ " } else if (typeof item.source === 'string') {",
4342
+ " for (const [from, to] of replacements) item.source = item.source.split(from).join(to);",
4343
+ " }",
4344
+ " }",
4345
+ " },",
4346
+ " };",
4347
+ "}",
4348
+ "",
4349
+ "export default defineConfig(() => {",
4350
+ " const isStagingBuild =",
4351
+ " process.env.DEPLOY_ENVIRONMENT === 'staging' ||",
4352
+ " process.env.TOPOLO_ENV === 'staging' ||",
4353
+ " process.env.VITE_TOPOLO_ENV === 'staging' ||",
4354
+ " process.env.VITE_TOPOLO_AUTH_URL === 'https://auth.stg.topolo.us';",
4355
+ "",
4356
+ " return {",
4357
+ " plugins: [react(), ...(isStagingBuild ? [stagingUrlRewrite()] : [])],",
4358
+ " resolve: {",
4359
+ " alias: {",
4360
+ " '@topolo-io/auth-client': fileURLToPath(new URL('./node_modules/@topolo-io/auth-client/dist/index.js', import.meta.url)),",
4361
+ " },",
4362
+ " dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime'],",
4363
+ " },",
4364
+ " build: { outDir: 'dist' },",
4365
+ " };",
3377
4366
  "});",
3378
4367
  ""
3379
4368
  ].join("\n");
@@ -3399,6 +4388,7 @@ function mainTsx() {
3399
4388
  return [
3400
4389
  "import React from 'react';",
3401
4390
  "import { createRoot } from 'react-dom/client';",
4391
+ "import '@topolo-io/design-tokens/tokens.css';",
3402
4392
  "import App from './App';",
3403
4393
  "import './styles.css';",
3404
4394
  "",
@@ -3418,12 +4408,15 @@ function mainTsx() {
3418
4408
  }
3419
4409
  function stylesCss() {
3420
4410
  return [
4411
+ "/* Local app styles. Design tokens come from @topolo-io/design-tokens (imported in main.tsx). */",
4412
+ "/* Use hsl(var(--token)) for colors so light/dark themes flip from the shared shell. */",
4413
+ "",
3421
4414
  ":root {",
3422
4415
  " color-scheme: light dark;",
3423
4416
  " font-family:",
3424
4417
  ' Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;',
3425
- " background: #f7f8fb;",
3426
- " color: #182032;",
4418
+ " background: hsl(var(--background));",
4419
+ " color: hsl(var(--foreground));",
3427
4420
  "}",
3428
4421
  "",
3429
4422
  "* {",
@@ -3432,7 +4425,8 @@ function stylesCss() {
3432
4425
  "",
3433
4426
  "body {",
3434
4427
  " margin: 0;",
3435
- " background: #f7f8fb;",
4428
+ " background: hsl(var(--background));",
4429
+ " color: hsl(var(--foreground));",
3436
4430
  "}",
3437
4431
  "",
3438
4432
  "button,",
@@ -3442,10 +4436,24 @@ function stylesCss() {
3442
4436
  " font: inherit;",
3443
4437
  "}",
3444
4438
  "",
3445
- ".topolo-starter-workspace,",
4439
+ ".topolo-starter-workspace {",
4440
+ " display: grid;",
4441
+ " gap: 18px;",
4442
+ " min-width: 0;",
4443
+ " width: auto;",
4444
+ " max-width: min(100%, calc(100vw - 56px));",
4445
+ " overflow-x: clip;",
4446
+ "}",
4447
+ "",
3446
4448
  ".topolo-starter-card-stack {",
3447
4449
  " display: grid;",
3448
4450
  " gap: 18px;",
4451
+ " min-width: 0;",
4452
+ "}",
4453
+ "",
4454
+ ".topolo-starter-workspace > *,",
4455
+ ".topolo-starter-card-stack > * {",
4456
+ " min-width: 0;",
3449
4457
  "}",
3450
4458
  "",
3451
4459
  ".topolo-starter-card h2 {",
@@ -3456,15 +4464,16 @@ function stylesCss() {
3456
4464
  "",
3457
4465
  ".topolo-starter-card p {",
3458
4466
  " margin: 0;",
3459
- " color: #526071;",
4467
+ " color: hsl(var(--muted-foreground));",
3460
4468
  " line-height: 1.55;",
3461
4469
  "}",
3462
4470
  "",
3463
4471
  ".topolo-starter-card {",
3464
- " border: 1px solid rgba(24, 32, 50, 0.12);",
3465
- " border-radius: 8px;",
4472
+ " border: 1px solid hsl(var(--border));",
4473
+ " border-radius: var(--radius);",
3466
4474
  " padding: 20px;",
3467
- " background: #ffffff;",
4475
+ " background: hsl(var(--card));",
4476
+ " color: hsl(var(--card-foreground));",
3468
4477
  "}",
3469
4478
  ""
3470
4479
  ].join("\n");
@@ -3479,9 +4488,13 @@ function platformAppTsx(application, options) {
3479
4488
  const tagline = options.audience === "personal" ? "Topolo personal app" : "Topolo workspace";
3480
4489
  const workspaceTitle = options.audience === "personal" ? "Personal app" : "Workspace";
3481
4490
  return [
3482
- "import { useEffect, useMemo, useState } from 'react';",
3483
- "import { createBrowserAuthModule, type AuthUser } from '@topolo/auth-client';",
3484
- "import { LandingPage, LoginPage, TopoloAppShell, TopoloAuthFlowScreen, TopoloLoadingScreen, type TopoloAppShellUser } from '@topolo/ui-kit';",
4491
+ "import { Component, useEffect, useMemo, useState, type ErrorInfo, type ReactNode } from 'react';",
4492
+ "import { createBrowserAuthModule, type AuthUser } from '@topolo-io/auth-client';",
4493
+ "import { LandingPage, LoginPage, TopoloAppShell, TopoloAuthFlowScreen, TopoloLoadingScreen, setupBugReporterErrorCapture, type TopoloAppShellUser } from '@topolo-io/ui-kit';",
4494
+ "",
4495
+ "// Wire global error/console capture so BugReporter (enabled in TopoloAppShell below)",
4496
+ "// can attach the last few errors to any submitted bug report.",
4497
+ "setupBugReporterErrorCapture();",
3485
4498
  "",
3486
4499
  "type NavItem = 'home';",
3487
4500
  "type AppRoute = { kind: 'root' } | { kind: 'login' } | { kind: 'callback' };",
@@ -3489,7 +4502,11 @@ function platformAppTsx(application, options) {
3489
4502
  `const APP_ID = '${application.id}';`,
3490
4503
  `const APP_NAME = '${escapeSingleQuoted(appName)}';`,
3491
4504
  `const DEFAULT_SERVICE_ID = '${serviceId}';`,
3492
- "const AUTH_URL = import.meta.env.VITE_TOPOLO_AUTH_URL ?? 'https://auth.topolo.app';",
4505
+ "const DEFAULT_AUTH_URL =",
4506
+ " typeof window !== 'undefined' && window.location.hostname.endsWith('.stg.topolo.us')",
4507
+ " ? 'https://auth.stg.topolo.us'",
4508
+ " : 'https://auth.topolo.app';",
4509
+ "const AUTH_URL = import.meta.env.VITE_TOPOLO_AUTH_URL ?? DEFAULT_AUTH_URL;",
3493
4510
  "const SERVICE_ID = import.meta.env.VITE_TOPOLO_SERVICE_ID ?? DEFAULT_SERVICE_ID;",
3494
4511
  "const LANDING_CONFIG = {",
3495
4512
  " app_name: APP_NAME,",
@@ -3534,6 +4551,14 @@ function platformAppTsx(application, options) {
3534
4551
  "}",
3535
4552
  "",
3536
4553
  "export default function App() {",
4554
+ " return (",
4555
+ " <AppErrorBoundary>",
4556
+ " <AppRoot />",
4557
+ " </AppErrorBoundary>",
4558
+ " );",
4559
+ "}",
4560
+ "",
4561
+ "function AppRoot() {",
3537
4562
  " const route = getRoute();",
3538
4563
  " const [user, setUser] = useState<AuthUser | null>(null);",
3539
4564
  " const [loading, setLoading] = useState(true);",
@@ -3678,8 +4703,8 @@ function platformAppTsx(application, options) {
3678
4703
  " user={shellUser}",
3679
4704
  ' activeNavItemId="home"',
3680
4705
  ` navItems={[{ id: 'home', label: '${escapeSingleQuoted(workspaceTitle)}' }]}`,
4706
+ ' navigationMode="topbar"',
3681
4707
  " onNavItemSelect={() => undefined}",
3682
- " eyebrow={APP_NAME}",
3683
4708
  ` title="${escapeDoubleQuoted(workspaceTitle)}"`,
3684
4709
  " authUrl={AUTH_URL}",
3685
4710
  " getAccessToken={getAccessToken}",
@@ -3691,7 +4716,6 @@ function platformAppTsx(application, options) {
3691
4716
  " bugReporterAppName={APP_NAME}",
3692
4717
  " enableCommandPalette",
3693
4718
  " enableBugReporter",
3694
- " showThemeToggle",
3695
4719
  " >",
3696
4720
  ' <div className="topolo-starter-workspace">',
3697
4721
  ' <section className="topolo-starter-card">',
@@ -3716,6 +4740,36 @@ function platformAppTsx(application, options) {
3716
4740
  " if (error instanceof Error && error.message) return error.message;",
3717
4741
  " return 'Unexpected authentication error.';",
3718
4742
  "}",
4743
+ "",
4744
+ "// Minimal error boundary so a render-time crash falls back to a recoverable surface",
4745
+ "// instead of a blank page. The shared TopoloAppShell BugReporter still has access to",
4746
+ "// the captured stack via setupBugReporterErrorCapture().",
4747
+ "class AppErrorBoundary extends Component<{ children: ReactNode }, { error: Error | null }> {",
4748
+ " state = { error: null as Error | null };",
4749
+ " static getDerivedStateFromError(error: Error) {",
4750
+ " return { error };",
4751
+ " }",
4752
+ " componentDidCatch(error: Error, info: ErrorInfo) {",
4753
+ " // Surface to the platform error pipeline. setupBugReporterErrorCapture() also",
4754
+ " // records this via the console.error hook for BugReporter attachments.",
4755
+ " console.error('[' + APP_ID + '] render error:', error, info.componentStack);",
4756
+ " }",
4757
+ " render() {",
4758
+ " if (!this.state.error) return this.props.children;",
4759
+ " return (",
4760
+ " <TopoloAuthFlowScreen",
4761
+ " appName={APP_NAME}",
4762
+ " appId={APP_ID}",
4763
+ " serviceId={SERVICE_ID}",
4764
+ ' status="error"',
4765
+ " title={APP_NAME + ' hit an unexpected error'}",
4766
+ " message={this.state.error.message}",
4767
+ ' actionLabel="Reload"',
4768
+ " onAction={() => window.location.reload()}",
4769
+ " />",
4770
+ " );",
4771
+ " }",
4772
+ "}",
3719
4773
  ""
3720
4774
  ].join("\n");
3721
4775
  }
@@ -3902,8 +4956,10 @@ function workerTs(application, target, _options) {
3902
4956
  }
3903
4957
  function platformWorkerTs(application) {
3904
4958
  const serviceId = application.serviceId ?? application.id;
4959
+ const serviceName = toTitle(application.name);
3905
4960
  return [
3906
- "import { verifyRequest } from '@topolo/auth-middleware';",
4961
+ "import { createTopoloWidgetResponse } from '@topolo/sdk';",
4962
+ "import { verifyRequest } from '@topolo-io/auth-middleware';",
3907
4963
  "",
3908
4964
  "export interface Env {",
3909
4965
  " AUTH_BASE_URL?: string;",
@@ -3931,6 +4987,22 @@ function platformWorkerTs(application) {
3931
4987
  " return json({ error: auth.error }, { status: auth.status }, request);",
3932
4988
  " }",
3933
4989
  "",
4990
+ " if (url.pathname === '/api/widget') {",
4991
+ " return json(createTopoloWidgetResponse({",
4992
+ " serviceId: env.SERVICE_ID ?? DEFAULT_SERVICE_ID,",
4993
+ ` serviceName: '${escapeSingleQuoted(serviceName)}',`,
4994
+ " widgets: [",
4995
+ " {",
4996
+ " type: 'quick-actions',",
4997
+ " actions: [",
4998
+ " { id: 'open-workspace', label: 'Open workspace', link: '/', primary: true },",
4999
+ " { id: 'view-health', label: 'View health', link: '/api/health' },",
5000
+ " ],",
5001
+ " },",
5002
+ " ],",
5003
+ " }), { status: 200 }, request);",
5004
+ " }",
5005
+ "",
3934
5006
  " if (url.pathname === '/api/bootstrap') {",
3935
5007
  " return json({",
3936
5008
  " user: auth.user,",
@@ -4109,14 +5181,14 @@ function systemJson(application, options) {
4109
5181
  slug: `topolo-${slug}`,
4110
5182
  kind: "application",
4111
5183
  summary: `${toTitle(application.name)} is a ${summaryLabel} scaffolded on shared Auth, UI Kit, CloudControl, and conformance contracts.`,
4112
- lifecycle: "planned",
5184
+ lifecycle: "inventory",
4113
5185
  last_verified: CURRENT_VERIFICATION_DATE,
4114
5186
  owners: ["platform-engineering"],
4115
5187
  repo_paths: [`PlatformApplications/${application.appDir}`],
4116
5188
  service_ids: options.api && application.serviceId ? [application.serviceId] : [],
4117
5189
  visibility: "internal",
4118
5190
  api_contract: {
4119
- type: options.api ? "worker" : "none",
5191
+ type: options.api ? "curated" : "none",
4120
5192
  source: options.api ? `PlatformApplications/${application.appDir}/src/worker.ts` : `PlatformApplications/${application.appDir}/src/App.tsx`,
4121
5193
  notes: options.api ? "Protected Worker routes validate Topolo Auth credentials and derive organization context from the credential. The browser baseline starts on shared landing and login routes, completes auth on /auth/callback, and dedupes React when linked Topolo packages are installed locally." : "Browser-only application surface using shared Topolo Auth landing/login/callback routes, shared shell packages, and React runtime dedupe."
4122
5194
  },
@@ -4124,7 +5196,7 @@ function systemJson(application, options) {
4124
5196
  doc_paths: [`internal/apps/${slug}`, `public/applications/${slug}`],
4125
5197
  security_assurance: {
4126
5198
  risk_tier: "internal",
4127
- auth_boundary: "Topolo Auth through @topolo/auth-client for browser sessions and @topolo/auth-middleware for Worker requests.",
5199
+ auth_boundary: "Topolo Auth through @topolo-io/auth-client for browser sessions and @topolo-io/auth-middleware for Worker requests.",
4128
5200
  tenant_isolation: options.tenancy === "household" ? "household_scoped" : "organization_scoped",
4129
5201
  external_inputs: ["browser", ...options.api ? ["api"] : []],
4130
5202
  sensitive_data: ["identity", "org_data"],
@@ -4148,7 +5220,7 @@ function internalDocs(application, options) {
4148
5220
  `title: "${escapeFrontmatter(title)} Handbook"`,
4149
5221
  `summary: "Internal handbook for ${escapeFrontmatter(title)}."`,
4150
5222
  'owners: ["platform-engineering"]',
4151
- 'status: "planned"',
5223
+ 'status: "draft"',
4152
5224
  `last_verified: "${CURRENT_VERIFICATION_DATE}"`,
4153
5225
  "source_repos:",
4154
5226
  ` - "PlatformApplications/${application.appDir}"`,
@@ -4171,7 +5243,7 @@ function internalDocs(application, options) {
4171
5243
  "",
4172
5244
  "## Architecture",
4173
5245
  "",
4174
- "The generated browser surface uses `@topolo/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.",
5246
+ "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.",
4175
5247
  "",
4176
5248
  "## Runtime Surfaces",
4177
5249
  "",
@@ -4185,7 +5257,7 @@ function internalDocs(application, options) {
4185
5257
  "",
4186
5258
  "## API Reference",
4187
5259
  "",
4188
- options.api ? "The generated API baseline exposes `/api/health` and `/api/bootstrap`. `/api/bootstrap` requires Topolo Auth validation and returns the verified user plus organization context." : "This generated baseline is browser-only and does not expose a callable API service.",
5260
+ options.api ? "The generated API baseline exposes `/api/health`, `/api/widget`, and `/api/bootstrap`. `/api/widget` returns the SDK widget response contract for TopoloOne, while `/api/bootstrap` requires Topolo Auth validation and returns the verified user plus organization context." : "This generated baseline is browser-only and does not expose a callable API service.",
4189
5261
  "",
4190
5262
  "## Auth and Permissions",
4191
5263
  "",
@@ -4232,7 +5304,7 @@ function publicDocs(application, options) {
4232
5304
  `title: "${escapeFrontmatter(title)}"`,
4233
5305
  `summary: "Public overview of ${escapeFrontmatter(title)} in the Topolo application suite."`,
4234
5306
  'owners: ["platform-engineering"]',
4235
- 'status: "planned"',
5307
+ 'status: "draft"',
4236
5308
  `last_verified: "${CURRENT_VERIFICATION_DATE}"`,
4237
5309
  "source_repos:",
4238
5310
  ` - "PlatformApplications/${application.appDir}"`,
@@ -4449,6 +5521,12 @@ function normalizeApplicationId(application) {
4449
5521
  "Application id must be kebab-case or snake_case using lowercase letters, numbers, hyphens, and underscores."
4450
5522
  );
4451
5523
  }
5524
+ function normalizeFirstPartyApplicationId(applicationId) {
5525
+ if (applicationId.startsWith("topolo_") && applicationId.length > "topolo_".length) {
5526
+ return applicationId.slice("topolo_".length);
5527
+ }
5528
+ return applicationId;
5529
+ }
4452
5530
  function toSlug(value) {
4453
5531
  return value.replace(/_/g, "-");
4454
5532
  }
@@ -4461,9 +5539,9 @@ function toTitle(value) {
4461
5539
  function json(value) {
4462
5540
  return JSON.stringify(value, null, 2);
4463
5541
  }
4464
- async function pathExists(path2) {
5542
+ async function pathExists(path4) {
4465
5543
  try {
4466
- await access(path2, constants.F_OK);
5544
+ await access(path4, constants.F_OK);
4467
5545
  return true;
4468
5546
  } catch {
4469
5547
  return false;
@@ -4694,6 +5772,17 @@ function registerAppsCommand(program2) {
4694
5772
  fail(err, mode);
4695
5773
  }
4696
5774
  });
5775
+ apps.command("record-evidence <application>").description(
5776
+ "Stamp <repo>/.topolo/evidence.json with the latest CI gate results so the audit can flip verification-gates / observability-and-audit / canonical-docs from partial to met when the recorded run is fresh (< 24h)."
5777
+ ).option("--json", "Output JSON", true).option("--no-json", "Output human-readable summary").option("--app-root <path>", "Path to the application repo root (defaults to cwd)").option("--build", "Mark the latest build as successful").option("--typecheck", "Mark the latest typecheck as successful").option("--test", "Mark the latest test run as successful").option("--docs-validate", "Mark the latest TopoloDocs npm run validate as successful").option("--smoke <url>", "Mark the latest live smoke as successful against this URL").option("--commit <sha>", "Override the commit hash (defaults to git HEAD)").action((application, opts) => {
5778
+ const mode = resolveOutputMode(opts.json);
5779
+ try {
5780
+ const payload = recordApplicationEvidence(application, opts);
5781
+ emit(payload, mode, () => formatEvidence(payload));
5782
+ } catch (err) {
5783
+ fail(err, mode);
5784
+ }
5785
+ });
4697
5786
  apps.command("scaffold <application>").description(
4698
5787
  "Generate a third-party Topolo application scaffold for business or personal/household use."
4699
5788
  ).option("--json", "Output JSON").option("--no-json", "Output human-readable checklist").option("--app-dir <path>", "Application directory under PlatformApplications").option("--name <name>", "Application display/package source name").option("--package-name <name>", "package.json name").option("--production-url <url>", "Production browser URL").option("--service <id>", "Stable SDK/Auth service id for API-backed apps").option("--audience <audience>", "Developer app audience: business or personal").option("--tenancy <tenancy>", "Developer app tenancy: organization or household").option(
@@ -4783,6 +5872,78 @@ function applicationAuditPayload(application, options = {}) {
4783
5872
  if (!options.local) return report;
4784
5873
  return enrichApplicationAuditWithLocalEvidence(report, { repoRoot: options.repoRoot });
4785
5874
  }
5875
+ function recordApplicationEvidence(application, options = {}) {
5876
+ const appId = resolveApplication(application).id;
5877
+ const root = path2.resolve(options.appRoot ?? process.cwd());
5878
+ if (!existsSync3(root)) {
5879
+ throw new Error(`Application root does not exist: ${root}`);
5880
+ }
5881
+ let commit = options.commit;
5882
+ if (!commit) {
5883
+ try {
5884
+ commit = execSync("git rev-parse HEAD", { cwd: root, stdio: ["ignore", "pipe", "ignore"] }).toString().trim() || void 0;
5885
+ } catch {
5886
+ commit = void 0;
5887
+ }
5888
+ }
5889
+ const evidenceDir = path2.join(root, ".topolo");
5890
+ const evidencePath = path2.join(evidenceDir, "evidence.json");
5891
+ let before = null;
5892
+ if (existsSync3(evidencePath)) {
5893
+ try {
5894
+ const raw = JSON.parse(readFileSync2(evidencePath, "utf8"));
5895
+ if (raw && typeof raw === "object" && raw.version === 1) {
5896
+ before = raw;
5897
+ }
5898
+ } catch {
5899
+ }
5900
+ }
5901
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5902
+ const after = before ? { ...before } : { version: 1, appId };
5903
+ after.version = 1;
5904
+ after.appId = appId;
5905
+ const updatedFields = [];
5906
+ const stamp = (ok) => ({ timestamp: now, ok, ...commit ? { commit } : {} });
5907
+ if (options.build) {
5908
+ after.lastBuild = stamp(true);
5909
+ updatedFields.push("lastBuild");
5910
+ }
5911
+ if (options.typecheck) {
5912
+ after.lastTypecheck = stamp(true);
5913
+ updatedFields.push("lastTypecheck");
5914
+ }
5915
+ if (options.test) {
5916
+ after.lastTest = stamp(true);
5917
+ updatedFields.push("lastTest");
5918
+ }
5919
+ if (options.docsValidate) {
5920
+ after.lastDocsValidate = { timestamp: now, ok: true };
5921
+ updatedFields.push("lastDocsValidate");
5922
+ }
5923
+ if (options.smoke) {
5924
+ after.lastSmoke = { timestamp: now, ok: true, url: options.smoke };
5925
+ updatedFields.push("lastSmoke");
5926
+ }
5927
+ if (updatedFields.length === 0) {
5928
+ throw new Error(
5929
+ "record-evidence: pass at least one of --build, --typecheck, --test, --docs-validate, --smoke <url>"
5930
+ );
5931
+ }
5932
+ mkdirSync(evidenceDir, { recursive: true });
5933
+ writeFileSync(evidencePath, JSON.stringify(after, null, 2) + "\n");
5934
+ return { appId, evidencePath, before, after, updatedFields };
5935
+ }
5936
+ function formatEvidence(result) {
5937
+ const lines = [];
5938
+ lines.push(`Stamped ${result.evidencePath}`);
5939
+ lines.push(` app: ${result.appId}`);
5940
+ for (const field of result.updatedFields) {
5941
+ const stamp = result.after[field];
5942
+ if (!stamp) continue;
5943
+ lines.push(` ${field}: ok at ${stamp.timestamp}${stamp.commit ? ` (${stamp.commit.slice(0, 7)})` : ""}`);
5944
+ }
5945
+ return lines.join("\n");
5946
+ }
4786
5947
  function evaluateApplicationAuditGate(payload, failOn) {
4787
5948
  const threshold = normalizeFailOn(failOn);
4788
5949
  const counts = {
@@ -5006,6 +6167,8 @@ function statusesAtOrWorse(threshold) {
5006
6167
  }
5007
6168
 
5008
6169
  // src/commands/platform.ts
6170
+ import { readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
6171
+ import path3 from "path";
5009
6172
  function registerPlatformCommand(program2) {
5010
6173
  const platform3 = program2.command("platform").description("Internal Topolo first-party platform and personal app commands");
5011
6174
  const platformApps = platform3.command("apps").description("Scaffold and operate first-party Topolo platform and personal applications");
@@ -5017,6 +6180,9 @@ function registerPlatformCommand(program2) {
5017
6180
  ).option("--container-app-id <appId>", "Container app id when --surface-model mobile_module is used").option("--write", "Write the generated scaffold files to disk").option("--repo-root <path>", "Directory where scaffold files should be written").option("--force", "Overwrite existing scaffold files when --write is set").option("--with-api", "Include the API/service baseline").option("--no-api", "Do not include API/service requirements").option("--no-browser", "Do not include browser-shell requirements").option(
5018
6181
  "--provision",
5019
6182
  "Provision the generated first-party app into Topolo Developers and Auth using the authenticated Topolo admin session"
6183
+ ).option(
6184
+ "--no-provision",
6185
+ "Skip live Developers/Auth registration when writing a first-party scaffold"
5020
6186
  ).option("--workflow-only", "Output only the generated GitHub Actions workflow in human mode").action(async (application, opts) => {
5021
6187
  const mode = opts.json === false || process.argv.includes("--no-json") ? "pretty" : resolveOutputMode(opts.json);
5022
6188
  try {
@@ -5041,11 +6207,13 @@ function registerPlatformCommand(program2) {
5041
6207
  if (opts.workflowOnly && !payload.ciWorkflow) {
5042
6208
  throw new Error("--workflow-only is only available for first-party scaffolds.");
5043
6209
  }
5044
- if (opts.provision && !opts.write) {
6210
+ const provisionRequested = process.argv.includes("--provision");
6211
+ const shouldProvision = Boolean(opts.write) && opts.provision !== false;
6212
+ if (provisionRequested && !opts.write) {
5045
6213
  throw new Error("--provision requires --write so remote first-party records match written scaffold files.");
5046
6214
  }
5047
6215
  const writeResult = opts.write ? await writeApplicationScaffold(payload, { repoRoot: opts.repoRoot, force: opts.force }) : null;
5048
- const provisionResult = opts.provision ? await provisionApplicationScaffold(payload) : null;
6216
+ const provisionResult = shouldProvision ? await provisionApplicationScaffold(payload) : null;
5049
6217
  const output = opts.workflowOnly ? { application: payload.application, ciWorkflowPath: payload.ciWorkflowPath, ciWorkflow: payload.ciWorkflow } : {
5050
6218
  ...payload,
5051
6219
  ...writeResult ? { write: writeResult } : {},
@@ -5060,6 +6228,95 @@ function registerPlatformCommand(program2) {
5060
6228
  fail(err, mode);
5061
6229
  }
5062
6230
  });
6231
+ const guard = platform3.command("guard").description("Run first-party platform guardrails");
6232
+ guard.command("staging-urls [root]").description("Fail when staging runtime source contains production Topolo URLs").option("--json", "Output JSON").option("--allow <pattern...>", "Additional regex allowlist patterns").action((root = process.cwd(), opts) => {
6233
+ const mode = opts.json ? "json" : "pretty";
6234
+ const result = scanStagingProductionUrls(root, opts.allow ?? []);
6235
+ if (mode === "json") {
6236
+ emit(result, "json", () => "");
6237
+ } else if (result.violations.length === 0) {
6238
+ emit(null, "pretty", () => `No staging production URL violations found in ${result.root}.`);
6239
+ } else {
6240
+ emit(
6241
+ null,
6242
+ "pretty",
6243
+ () => [
6244
+ `Found ${result.violations.length} staging production URL violation(s) in ${result.root}:`,
6245
+ ...result.violations.map(
6246
+ (violation) => `- ${violation.file}:${violation.line} ${violation.url}`
6247
+ )
6248
+ ].join("\n")
6249
+ );
6250
+ }
6251
+ if (result.violations.length > 0) process.exitCode = 1;
6252
+ });
6253
+ }
6254
+ var PRODUCTION_TOPOLO_URL_PATTERN = /(https?:\/\/[^\s'"`)>}]*topolo\.(?:app|io)[^\s'"`)<}]*)|(?:[a-z0-9-]+\.topolo\.app)/gi;
6255
+ var STAGING_TOPOLO_URL_PATTERN = /\.stg\.topolo\.us\b/;
6256
+ var RUNTIME_EXTENSIONS = /* @__PURE__ */ new Set([".cjs", ".html", ".js", ".jsx", ".json", ".jsonc", ".mjs", ".toml", ".ts", ".tsx"]);
6257
+ var SKIPPED_SCAN_DIRS = /* @__PURE__ */ new Set([
6258
+ ".git",
6259
+ ".nexus-widget-deploy",
6260
+ ".next",
6261
+ ".playwright-cli",
6262
+ ".playwright-mcp",
6263
+ ".wrangler",
6264
+ ".worktrees",
6265
+ "_staging-clones",
6266
+ "build",
6267
+ "coverage",
6268
+ "dist",
6269
+ "node_modules",
6270
+ "out",
6271
+ "output"
6272
+ ]);
6273
+ function scanStagingProductionUrls(rootInput, allowPatterns) {
6274
+ const root = path3.resolve(rootInput);
6275
+ const allowlist = allowPatterns.map((pattern) => new RegExp(pattern));
6276
+ const violations = [];
6277
+ function shouldSkipFile(filePath) {
6278
+ const basename = path3.basename(filePath);
6279
+ const relativePath = path3.relative(root, filePath);
6280
+ if (basename === "package-lock.json" || basename === "pnpm-lock.yaml" || basename === "yarn.lock") return true;
6281
+ if (basename === "topolo.cloudcontrol.json") return true;
6282
+ if (basename === "wrangler.toml" || basename === "wrangler.jsonc") return true;
6283
+ if (basename === "consumer-graph.json") return true;
6284
+ if (/^cloudcontrol-deployment-report-.+\.json$/.test(basename)) return true;
6285
+ if (basename.endsWith(".generated.ts")) return true;
6286
+ if (basename.endsWith(".test.ts") || basename.endsWith(".test.tsx") || basename.endsWith(".spec.ts")) return true;
6287
+ if (basename === ".env.production") return true;
6288
+ if (relativePath === path3.join("src", "environment.ts") || relativePath === path3.join("src", "environmentUrls.ts") || relativePath.endsWith(path3.join("src", "environment.ts")) || relativePath.endsWith(path3.join("src", "lib", "environment.ts")) || relativePath === path3.join("packages", "topolo-ui-kit", "src", "environmentUrls.ts")) return true;
6289
+ if (relativePath.includes(`${path3.sep}test${path3.sep}`) || relativePath.includes(`${path3.sep}tests${path3.sep}`) || relativePath.includes(`${path3.sep}e2e${path3.sep}`)) return true;
6290
+ if (relativePath.startsWith(`scripts${path3.sep}`) || relativePath.includes(`${path3.sep}scripts${path3.sep}`)) return true;
6291
+ return !RUNTIME_EXTENSIONS.has(path3.extname(filePath));
6292
+ }
6293
+ function scanFile(filePath) {
6294
+ if (shouldSkipFile(filePath)) return;
6295
+ const relativePath = path3.relative(root, filePath);
6296
+ const content = readFileSync3(filePath, "utf8");
6297
+ const lines = content.split(/\r?\n/);
6298
+ lines.forEach((line, index) => {
6299
+ const matches = [...line.matchAll(PRODUCTION_TOPOLO_URL_PATTERN)].map((match) => match[0]);
6300
+ for (const url of new Set(matches)) {
6301
+ const allowed = STAGING_TOPOLO_URL_PATTERN.test(line) || line.includes("PRODUCTION_") || line.includes("hostname ===") || line.includes(".split(") && line.includes(".join(") || allowlist.some((pattern) => pattern.test(`${relativePath}:${index + 1}:${line}`));
6302
+ if (!allowed) {
6303
+ violations.push({ file: relativePath, line: index + 1, url, text: line.trim() });
6304
+ }
6305
+ }
6306
+ });
6307
+ }
6308
+ function walk(dir) {
6309
+ for (const entry of readdirSync2(dir, { withFileTypes: true })) {
6310
+ const fullPath = path3.join(dir, entry.name);
6311
+ if (entry.isDirectory()) {
6312
+ if (!SKIPPED_SCAN_DIRS.has(entry.name)) walk(fullPath);
6313
+ continue;
6314
+ }
6315
+ if (entry.isFile() && statSync2(fullPath).size < 2e6) scanFile(fullPath);
6316
+ }
6317
+ }
6318
+ walk(root);
6319
+ return { root, violations };
5063
6320
  }
5064
6321
  function resolvePlatformPortfolio(value) {
5065
6322
  if (!value) return void 0;
@@ -5138,6 +6395,220 @@ function formatRows2(rows) {
5138
6395
  return lines.join("\n");
5139
6396
  }
5140
6397
 
6398
+ // src/commands/seed.ts
6399
+ var DEFAULT_SEED_BASE_URL = "https://seed.stg.topolo.us";
6400
+ function seedBaseUrl() {
6401
+ return process.env.TOPOLO_SEED_URL || DEFAULT_SEED_BASE_URL;
6402
+ }
6403
+ async function authedFetch(path4, init = {}) {
6404
+ const credential = await refreshCredentialIfNeeded();
6405
+ if (!credential) {
6406
+ throw new Error("No credential configured. Run `topolo auth login` or set TOPOLO_API_KEY.");
6407
+ }
6408
+ const headers = new Headers(init.headers || {});
6409
+ if (credential.kind === "api_key") headers.set("X-Api-Key", credential.apiKey);
6410
+ else headers.set("Authorization", `Bearer ${credential.accessToken}`);
6411
+ headers.set("X-Topolo-Client", `topolo-cli/${CLI_VERSION}`);
6412
+ if (init.body && !headers.has("Content-Type")) headers.set("Content-Type", "application/json");
6413
+ const response = await fetch(`${seedBaseUrl()}${path4}`, { ...init, headers });
6414
+ const text = await response.text();
6415
+ let body;
6416
+ try {
6417
+ body = text ? JSON.parse(text) : {};
6418
+ } catch {
6419
+ body = { raw: text };
6420
+ }
6421
+ if (!response.ok) {
6422
+ throw new Error(`TopoloSeed ${response.status}: ${typeof body === "object" && body && "error" in body ? body.error : text}`);
6423
+ }
6424
+ return body;
6425
+ }
6426
+ function registerSeedCommands(program2) {
6427
+ const seed = program2.command("seed").description("Drive TopoloSeed (staging only): list packs, queue runs, check status, reset.");
6428
+ seed.command("list").description("List seed packs from the staging catalog").option("--json", "Output JSON", true).option("--no-json", "Output table").option("--scope <scope>", "Filter by scope (system | application | feature)").option("--app <appId>", "Filter by app id (e.g. topolo-auth)").action(async (opts) => {
6429
+ const mode = resolveOutputMode(opts.json);
6430
+ try {
6431
+ const params = new URLSearchParams();
6432
+ if (opts.scope) params.set("scope", opts.scope);
6433
+ if (opts.app) params.set("app_id", opts.app);
6434
+ const qs = params.toString();
6435
+ const body = await authedFetch(`/api/seed-packs${qs ? `?${qs}` : ""}`);
6436
+ emit({ packs: body.seed_packs }, mode, () => formatPackTable(body.seed_packs));
6437
+ } catch (err) {
6438
+ fail(err, mode);
6439
+ }
6440
+ });
6441
+ seed.command("coverage").description("Show per-app coverage from the live view").option("--json", "Output JSON", true).option("--no-json", "Output table").action(async (opts) => {
6442
+ const mode = resolveOutputMode(opts.json);
6443
+ try {
6444
+ const body = await authedFetch("/api/coverage");
6445
+ emit({ coverage: body.coverage }, mode, () => formatCoverageTable(body.coverage));
6446
+ } catch (err) {
6447
+ fail(err, mode);
6448
+ }
6449
+ });
6450
+ seed.command("run <packId>").description("Queue a seed run; the cron executor picks it up within ~1 minute").option("--json", "Output JSON", true).option("--no-json", "Pretty print").action(async (packId, opts) => {
6451
+ const mode = resolveOutputMode(opts.json);
6452
+ try {
6453
+ const body = await authedFetch("/api/runs", { method: "POST", body: JSON.stringify({ pack_id: packId }) });
6454
+ emit({ run: body.run }, mode, () => `Queued ${body.run?.run_id || "unknown"}`);
6455
+ } catch (err) {
6456
+ fail(err, mode);
6457
+ }
6458
+ });
6459
+ seed.command("status [runId]").description("Show recent runs, or a specific run if runId is provided").option("--json", "Output JSON", true).option("--no-json", "Output table").action(async (runId, opts) => {
6460
+ const mode = resolveOutputMode(opts.json);
6461
+ try {
6462
+ if (runId) {
6463
+ const body = await authedFetch(`/api/runs/${encodeURIComponent(runId)}`);
6464
+ emit({ run: body.run }, mode, () => formatRunDetail(body.run));
6465
+ } else {
6466
+ const body = await authedFetch("/api/runs");
6467
+ emit({ runs: body.runs }, mode, () => formatRunsTable(body.runs));
6468
+ }
6469
+ } catch (err) {
6470
+ fail(err, mode);
6471
+ }
6472
+ });
6473
+ seed.command("cancel <runId>").description("Cancel a queued or running seed run").option("--json", "Output JSON", true).action(async (runId, opts) => {
6474
+ const mode = resolveOutputMode(opts.json);
6475
+ try {
6476
+ const body = await authedFetch(`/api/runs/${encodeURIComponent(runId)}/cancel`, { method: "POST" });
6477
+ emit(body, mode, () => `Cancelled ${runId}`);
6478
+ } catch (err) {
6479
+ fail(err, mode);
6480
+ }
6481
+ });
6482
+ seed.command("retry <runId>").description("Retry a failed or cancelled run (re-queues failed steps)").option("--json", "Output JSON", true).action(async (runId, opts) => {
6483
+ const mode = resolveOutputMode(opts.json);
6484
+ try {
6485
+ const body = await authedFetch(`/api/runs/${encodeURIComponent(runId)}/retry`, { method: "POST" });
6486
+ emit(body, mode, () => `Retried ${runId}`);
6487
+ } catch (err) {
6488
+ fail(err, mode);
6489
+ }
6490
+ });
6491
+ const reset = seed.command("reset").description("Inspect or execute reset plans");
6492
+ reset.command("preview").description("Create a reset plan and return its confirmation phrase").option("--type <type>", "Reset type (soft_activity | app | cross_app)", "cross_app").option("--app <appId>", "Limit to a single target app").option("--feature <featureId>", "Limit to a feature").option("--no-preserve-identities", "Allow auth identities to be deleted").option("--json", "Output JSON", true).action(async (opts) => {
6493
+ const mode = resolveOutputMode(opts.json);
6494
+ try {
6495
+ const body = await authedFetch("/api/resets/preview", {
6496
+ method: "POST",
6497
+ body: JSON.stringify({
6498
+ reset_type: opts.type,
6499
+ target_scope: opts.app ? "application" : "system",
6500
+ target_app_id: opts.app || null,
6501
+ target_feature_id: opts.feature || null,
6502
+ preserve_auth_identities: opts.preserveIdentities !== false
6503
+ })
6504
+ });
6505
+ emit({ reset_plan: body.reset_plan }, mode, () => formatPlanPreview(body.reset_plan));
6506
+ } catch (err) {
6507
+ fail(err, mode);
6508
+ }
6509
+ });
6510
+ reset.command("execute <planId> <confirmation>").description("Execute a previously previewed reset plan (confirmation = the phrase from preview)").option("--dry-run", "Dispatch with dry_run=true to count rows without deleting").option("--json", "Output JSON", true).action(async (planId, confirmation, opts) => {
6511
+ const mode = resolveOutputMode(opts.json);
6512
+ try {
6513
+ const body = await authedFetch(`/api/resets/${encodeURIComponent(planId)}/execute`, {
6514
+ method: "POST",
6515
+ body: JSON.stringify({ confirmation, dry_run: opts.dryRun === true })
6516
+ });
6517
+ emit({ reset_plan: body.reset_plan }, mode, () => formatPlanResult(body.reset_plan));
6518
+ } catch (err) {
6519
+ fail(err, mode);
6520
+ }
6521
+ });
6522
+ seed.command("manifests").description("Show the cached __seed/manifest for each registered app").option("--refresh", "Force re-fetch all manifests now").option("--json", "Output JSON", true).action(async (opts) => {
6523
+ const mode = resolveOutputMode(opts.json);
6524
+ try {
6525
+ if (opts.refresh) {
6526
+ const refreshed = await authedFetch("/api/manifests/refresh", { method: "POST", body: JSON.stringify({}) });
6527
+ emit(refreshed, mode, () => "Refreshed all manifests");
6528
+ return;
6529
+ }
6530
+ const body = await authedFetch("/api/manifests");
6531
+ emit({ manifests: body.manifests }, mode, () => formatManifestsTable(body.manifests));
6532
+ } catch (err) {
6533
+ fail(err, mode);
6534
+ }
6535
+ });
6536
+ }
6537
+ function formatPackTable(packs) {
6538
+ if (packs.length === 0) return "(no packs)";
6539
+ const lines = [];
6540
+ lines.push("PACK_ID SCOPE APP NAME");
6541
+ for (const p of packs) {
6542
+ lines.push(`${String(p.pack_id || "").padEnd(32)} ${String(p.scope || "").padEnd(12)} ${String(p.app_id || "-").padEnd(19)} ${String(p.name || "")}`);
6543
+ }
6544
+ return lines.join("\n");
6545
+ }
6546
+ function formatCoverageTable(rows) {
6547
+ if (rows.length === 0) return "(no apps)";
6548
+ const lines = [];
6549
+ lines.push("APP COVERAGE D1 KV R2 QUEUES");
6550
+ for (const r of rows) {
6551
+ lines.push(`${String(r.app_id || "").padEnd(25)} ${String(r.coverage_status || "").padEnd(17)} ${String(r.d1_status || "").padEnd(13)} ${String(r.kv_status || "").padEnd(13)} ${String(r.r2_status || "").padEnd(13)} ${String(r.queue_status || "")}`);
6552
+ }
6553
+ return lines.join("\n");
6554
+ }
6555
+ function formatRunsTable(runs) {
6556
+ if (runs.length === 0) return "(no runs)";
6557
+ const lines = [];
6558
+ lines.push("RUN_ID PACK STATUS ATTEMPT");
6559
+ for (const r of runs) {
6560
+ lines.push(`${String(r.run_id || "").padEnd(40)} ${String(r.pack_id || "").padEnd(29)} ${String(r.status || "").padEnd(12)} ${String(r.attempt ?? 0)}`);
6561
+ }
6562
+ return lines.join("\n");
6563
+ }
6564
+ function formatRunDetail(run) {
6565
+ const steps = run.steps || [];
6566
+ const lines = [];
6567
+ lines.push(`run_id: ${run.run_id}`);
6568
+ lines.push(`pack: ${run.pack_id}`);
6569
+ lines.push(`status: ${run.status} (attempt ${run.attempt ?? 0})`);
6570
+ if (steps.length > 0) {
6571
+ lines.push("");
6572
+ lines.push("STEPS:");
6573
+ for (const s of steps) {
6574
+ lines.push(` [${s.status}] ${s.step_id} \u2192 ${s.app_id}${s.last_error ? ` (${s.last_error})` : ""}`);
6575
+ }
6576
+ }
6577
+ return lines.join("\n");
6578
+ }
6579
+ function formatPlanPreview(plan) {
6580
+ return [
6581
+ `reset_plan_id: ${plan.reset_plan_id}`,
6582
+ `confirmation: ${plan.confirmation_phrase}`,
6583
+ `estimated: ${plan.estimated_rows} rows / ${plan.estimated_objects} objects`,
6584
+ "",
6585
+ "To execute:",
6586
+ ` topolo seed reset execute ${plan.reset_plan_id} "${plan.confirmation_phrase}"`
6587
+ ].join("\n");
6588
+ }
6589
+ function formatPlanResult(plan) {
6590
+ const results = plan.results || [];
6591
+ const lines = [`reset_plan_id: ${plan.reset_plan_id}`, `status: ${plan.status}`, `total_deleted: ${plan.total_deleted ?? 0}`];
6592
+ if (results.length > 0) {
6593
+ lines.push("");
6594
+ lines.push("PER-APP:");
6595
+ for (const r of results) {
6596
+ lines.push(` [${r.status}] ${r.app_id}: ${r.rows_deleted ?? 0} rows${r.error ? ` (${r.error})` : ""}`);
6597
+ }
6598
+ }
6599
+ return lines.join("\n");
6600
+ }
6601
+ function formatManifestsTable(manifests) {
6602
+ if (manifests.length === 0) return "(no manifests cached)";
6603
+ const lines = [];
6604
+ lines.push("APP VERSION SOURCE FETCHED_AT ERROR");
6605
+ for (const m of manifests) {
6606
+ const fetched = m.fetched_at ? new Date(Number(m.fetched_at)).toISOString().replace("T", " ").slice(0, 19) : "";
6607
+ lines.push(`${String(m.app_id || "").padEnd(25)} ${String(m.contract_version ?? 0).padEnd(8)} ${String(m.source || "").padEnd(18)} ${fetched.padEnd(20)} ${String(m.fetch_error || "")}`);
6608
+ }
6609
+ return lines.join("\n");
6610
+ }
6611
+
5141
6612
  // src/index.ts
5142
6613
  var program = new Command();
5143
6614
  program.name("topolo").description(
@@ -5150,6 +6621,7 @@ registerApiCommand(program);
5150
6621
  registerAppsCommand(program);
5151
6622
  registerPlatformCommand(program);
5152
6623
  registerServicesCommand(program);
6624
+ registerSeedCommands(program);
5153
6625
  program.parseAsync(process.argv).catch((err) => {
5154
6626
  process.stderr.write(`Unexpected error: ${err instanceof Error ? err.message : String(err)}
5155
6627
  `);