@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.
- package/dist/index.js +1687 -215
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { platform as platform2 } from "os";
|
|
|
9
9
|
import { createInterface } from "readline/promises";
|
|
10
10
|
import { stdin as input } from "process";
|
|
11
11
|
|
|
12
|
-
//
|
|
12
|
+
// 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,
|
|
59
|
-
super("http_error", message ?? `HTTP ${status} from ${service}${
|
|
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 =
|
|
64
|
+
this.path = path4;
|
|
65
65
|
}
|
|
66
66
|
};
|
|
67
67
|
var SERVICES = {
|
|
68
|
-
agent: "https://
|
|
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://
|
|
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://
|
|
76
|
+
commerce: "https://commerce-api.topolo.app",
|
|
77
|
+
compose: "https://compose.topolo.app",
|
|
75
78
|
crm: "https://crm-api.topolo.app",
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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://
|
|
90
|
+
mdm: "https://mdm-api.topolo.app",
|
|
81
91
|
messaging: "https://messages.topolo.app",
|
|
82
|
-
nexus: "https://
|
|
92
|
+
nexus: "https://nexus-api.topolo.app",
|
|
93
|
+
notify: "https://notify.topolo.app",
|
|
83
94
|
one: "https://one.topolo.app",
|
|
84
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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://
|
|
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": "
|
|
277
|
+
"name": "topolo-bytes-api",
|
|
211
278
|
"kind": "worker",
|
|
212
|
-
"apiUrl": "https://
|
|
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": "
|
|
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://
|
|
389
|
+
"apiUrl": "https://commerce-api.topolo.app",
|
|
299
390
|
"domains": [
|
|
300
|
-
"
|
|
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-
|
|
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-
|
|
495
|
+
"name": "topolo-success",
|
|
360
496
|
"kind": "worker",
|
|
361
|
-
"apiUrl": "https://
|
|
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":
|
|
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":
|
|
413
|
-
"productionUrl": "https://
|
|
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://
|
|
611
|
+
"apiUrl": "https://feed-analytics-api.topolo.app",
|
|
422
612
|
"domains": [
|
|
423
|
-
"
|
|
613
|
+
"feed-analytics-api.topolo.app"
|
|
424
614
|
],
|
|
425
615
|
"routes": [
|
|
426
|
-
"
|
|
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://
|
|
623
|
+
"apiUrl": "https://feed-api.topolo.app",
|
|
434
624
|
"domains": [
|
|
435
|
-
"
|
|
625
|
+
"feed-api.topolo.app"
|
|
436
626
|
],
|
|
437
627
|
"routes": [
|
|
438
|
-
"
|
|
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-
|
|
679
|
+
"name": "topolo-forecast-api",
|
|
464
680
|
"kind": "worker",
|
|
465
|
-
"apiUrl": "https://forecast-
|
|
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": "
|
|
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": "
|
|
783
|
+
"name": "topolo-learn-api",
|
|
494
784
|
"kind": "worker",
|
|
495
|
-
"apiUrl": "https://
|
|
785
|
+
"apiUrl": "https://learn-api.topolo.app",
|
|
496
786
|
"domains": [
|
|
497
|
-
"
|
|
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":
|
|
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://
|
|
864
|
+
"apiUrl": "https://mdm-api.topolo.app",
|
|
551
865
|
"domains": [
|
|
552
|
-
"
|
|
866
|
+
"mdm-api.topolo.app"
|
|
553
867
|
],
|
|
554
868
|
"routes": [
|
|
555
|
-
"
|
|
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://
|
|
940
|
+
"apiUrl": "https://nexus-api.topolo.app",
|
|
629
941
|
"domains": [
|
|
630
|
-
"
|
|
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":
|
|
996
|
+
"apiUrl": "https://ai.one.topolo.app",
|
|
651
997
|
"domains": [
|
|
652
|
-
"ai
|
|
998
|
+
"ai.one.topolo.app"
|
|
653
999
|
],
|
|
654
1000
|
"routes": [],
|
|
655
|
-
"serviceId":
|
|
1001
|
+
"serviceId": "one.ai"
|
|
656
1002
|
},
|
|
657
1003
|
{
|
|
658
|
-
"name": "
|
|
1004
|
+
"name": "topolo-one",
|
|
659
1005
|
"kind": "pages",
|
|
660
1006
|
"apiUrl": null,
|
|
661
1007
|
"domains": [
|
|
662
|
-
"
|
|
1008
|
+
"one.topolo.app"
|
|
663
1009
|
],
|
|
664
1010
|
"routes": [],
|
|
665
1011
|
"serviceId": null
|
|
666
1012
|
},
|
|
667
1013
|
{
|
|
668
|
-
"name": "
|
|
1014
|
+
"name": "topolo-one-website",
|
|
669
1015
|
"kind": "worker",
|
|
670
1016
|
"apiUrl": null,
|
|
671
1017
|
"domains": [
|
|
672
|
-
"
|
|
1018
|
+
"topolo.io",
|
|
1019
|
+
"www.topolo.io"
|
|
673
1020
|
],
|
|
674
1021
|
"routes": [],
|
|
675
1022
|
"serviceId": null
|
|
676
1023
|
},
|
|
677
1024
|
{
|
|
678
|
-
"name": "
|
|
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://
|
|
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":
|
|
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-
|
|
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://
|
|
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-
|
|
1283
|
+
"name": "topolo-socialize-api",
|
|
836
1284
|
"kind": "worker",
|
|
837
|
-
"apiUrl": "https://socialize-
|
|
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-
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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-
|
|
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
|
|
1087
|
-
const
|
|
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
|
-
|
|
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" ?
|
|
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 (!
|
|
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
|
|
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:
|
|
1496
|
-
page:
|
|
1497
|
-
pageSize:
|
|
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(
|
|
1592
|
-
const url = new URL(
|
|
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",
|
|
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
|
|
1635
|
-
if (!existsSync(
|
|
2177
|
+
const path4 = configPath();
|
|
2178
|
+
if (!existsSync(path4)) return {};
|
|
1636
2179
|
try {
|
|
1637
|
-
const raw = await readFile(
|
|
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
|
|
1645
|
-
await mkdir(dirname(
|
|
1646
|
-
await writeFile(
|
|
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(
|
|
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,
|
|
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:
|
|
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:
|
|
2265
|
-
reason:
|
|
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: "
|
|
2278
|
-
reason: "Canonical system security assurance documents an operator-local auth boundary; shared auth package
|
|
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:
|
|
2293
|
-
evidence:
|
|
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
|
-
|
|
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: "
|
|
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: "
|
|
2340
|
-
reason
|
|
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)
|
|
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)
|
|
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
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
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,
|
|
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-
|
|
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
|
|
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/
|
|
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
|
|
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 {
|
|
4326
|
+
"import { fileURLToPath } from 'node:url';",
|
|
4327
|
+
"import { defineConfig, type Plugin } from 'vite';",
|
|
3369
4328
|
"import react from '@vitejs/plugin-react';",
|
|
3370
4329
|
"",
|
|
3371
|
-
"
|
|
3372
|
-
"
|
|
3373
|
-
"
|
|
3374
|
-
"
|
|
3375
|
-
"
|
|
3376
|
-
"
|
|
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:
|
|
3426
|
-
" color:
|
|
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:
|
|
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:
|
|
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
|
|
3465
|
-
" border-radius:
|
|
4472
|
+
" border: 1px solid hsl(var(--border));",
|
|
4473
|
+
" border-radius: var(--radius);",
|
|
3466
4474
|
" padding: 20px;",
|
|
3467
|
-
" background:
|
|
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
|
|
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 {
|
|
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: "
|
|
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 ? "
|
|
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: "
|
|
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
|
|
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: "
|
|
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(
|
|
5542
|
+
async function pathExists(path4) {
|
|
4465
5543
|
try {
|
|
4466
|
-
await access(
|
|
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
|
-
|
|
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 =
|
|
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
|
`);
|