@topolo/cli 0.1.11 → 0.1.12
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 +498 -322
- 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
|
+
// ../../../../../Users/ashes/Projects/topolo/PlatformApplications/packages/topolo-sdk/dist/index.js
|
|
13
13
|
function applyAuthHeaders(headers, credential) {
|
|
14
14
|
if (credential.kind === "api_key") {
|
|
15
15
|
headers.set("X-Api-Key", credential.apiKey);
|
|
@@ -67,20 +67,14 @@ var TopoloHttpError = class extends TopoloSdkError {
|
|
|
67
67
|
var SERVICES = {
|
|
68
68
|
agent: "https://topolo-agent-worker.topolo.workers.dev",
|
|
69
69
|
auth: "https://auth.topolo.app",
|
|
70
|
-
books: "https://books.topolo.app",
|
|
71
70
|
bugfix: "https://bugfix.topolo.app",
|
|
72
71
|
bytes: "https://r2-media-manager-worker.topolo.workers.dev",
|
|
73
72
|
calendar: "https://calendar.topolo.app",
|
|
74
73
|
chat: "https://chat.topolo.app",
|
|
75
74
|
commerce: "https://topolo-commerce-api.topolo.workers.dev",
|
|
76
75
|
crm: "https://crm-api.topolo.app",
|
|
77
|
-
customer_success: "https://customer-success.topolo.app",
|
|
78
76
|
feed: "https://topolo-feed-api.topolo.app",
|
|
79
|
-
flow: "https://flow.topolo.app",
|
|
80
77
|
forecast: "https://forecast-worker-production.topolo.workers.dev",
|
|
81
|
-
forms: "https://forms.topolo.app",
|
|
82
|
-
insights: "https://insights.topolo.app",
|
|
83
|
-
inventory: "https://inventory.topolo.app",
|
|
84
78
|
learn: "https://topololearn-api.topolo.workers.dev",
|
|
85
79
|
mail: "https://mail.topolo.app",
|
|
86
80
|
mdm: "https://topolo-mdm-api.topolo.app",
|
|
@@ -88,30 +82,16 @@ var SERVICES = {
|
|
|
88
82
|
nexus: "https://topolo-nexus-gateway.topolo.workers.dev",
|
|
89
83
|
one: "https://topoloone-worker.topolo.workers.dev",
|
|
90
84
|
pay: "https://topolo-pay.topolo.workers.dev",
|
|
91
|
-
people: "https://people.topolo.app",
|
|
92
85
|
"quro.api": "https://api.ol0.me",
|
|
93
86
|
"quro.redirect": "https://ol0.me",
|
|
94
87
|
roadmapper: "https://roadmapper-api.topolo.app",
|
|
95
|
-
sign: "https://sign.topolo.app",
|
|
96
88
|
social_studio: "https://topolo-social-studio-api.topolo.workers.dev",
|
|
97
89
|
socialize: "https://socialize-prod-api.topolo.workers.dev",
|
|
98
90
|
support: "https://support.topolo.app",
|
|
99
|
-
survey: "https://survey.topolo.app",
|
|
100
|
-
voice: "https://voice.topolo.app",
|
|
101
91
|
web: "https://web-api.topolo.app"
|
|
102
92
|
};
|
|
103
93
|
var PLATFORM_SERVICE_IDS = {
|
|
104
|
-
|
|
105
|
-
crm: "srv_iCwM4jGXcwlj",
|
|
106
|
-
customer_success: "srv_topolo_customer_success",
|
|
107
|
-
flow: "srv_topolo_flow",
|
|
108
|
-
forms: "srv_topolo_forms",
|
|
109
|
-
insights: "srv_topolo_insights",
|
|
110
|
-
inventory: "srv_topolo_inventory",
|
|
111
|
-
people: "srv_topolo_people",
|
|
112
|
-
sign: "srv_topolo_sign",
|
|
113
|
-
survey: "srv_topolo_survey",
|
|
114
|
-
voice: "srv_topolo_voice"
|
|
94
|
+
crm: "srv_iCwM4jGXcwlj"
|
|
115
95
|
};
|
|
116
96
|
var APPLICATIONS = {
|
|
117
97
|
"admin": {
|
|
@@ -184,30 +164,6 @@ var APPLICATIONS = {
|
|
|
184
164
|
}
|
|
185
165
|
]
|
|
186
166
|
},
|
|
187
|
-
"books": {
|
|
188
|
-
"id": "books",
|
|
189
|
-
"appDir": "TopoloBooks",
|
|
190
|
-
"name": "topolo-books",
|
|
191
|
-
"packageName": "topolo-books",
|
|
192
|
-
"productionUrl": "https://books.topolo.app",
|
|
193
|
-
"services": [
|
|
194
|
-
"books"
|
|
195
|
-
],
|
|
196
|
-
"deployTargets": [
|
|
197
|
-
{
|
|
198
|
-
"name": "topolo-books",
|
|
199
|
-
"kind": "worker",
|
|
200
|
-
"apiUrl": "https://books.topolo.app",
|
|
201
|
-
"domains": [
|
|
202
|
-
"books.topolo.app"
|
|
203
|
-
],
|
|
204
|
-
"routes": [
|
|
205
|
-
"books.topolo.app"
|
|
206
|
-
],
|
|
207
|
-
"serviceId": "books"
|
|
208
|
-
}
|
|
209
|
-
]
|
|
210
|
-
},
|
|
211
167
|
"bugfix": {
|
|
212
168
|
"id": "bugfix",
|
|
213
169
|
"appDir": "TopoloBugFix",
|
|
@@ -409,30 +365,6 @@ var APPLICATIONS = {
|
|
|
409
365
|
}
|
|
410
366
|
]
|
|
411
367
|
},
|
|
412
|
-
"customer_success": {
|
|
413
|
-
"id": "customer_success",
|
|
414
|
-
"appDir": "TopoloCustomerSuccess",
|
|
415
|
-
"name": "topolo-customer-success",
|
|
416
|
-
"packageName": "topolo-customer-success",
|
|
417
|
-
"productionUrl": "https://customer-success.topolo.app",
|
|
418
|
-
"services": [
|
|
419
|
-
"customer_success"
|
|
420
|
-
],
|
|
421
|
-
"deployTargets": [
|
|
422
|
-
{
|
|
423
|
-
"name": "topolo-customer-success",
|
|
424
|
-
"kind": "worker",
|
|
425
|
-
"apiUrl": "https://customer-success.topolo.app",
|
|
426
|
-
"domains": [
|
|
427
|
-
"customer-success.topolo.app"
|
|
428
|
-
],
|
|
429
|
-
"routes": [
|
|
430
|
-
"customer-success.topolo.app"
|
|
431
|
-
],
|
|
432
|
-
"serviceId": "customer_success"
|
|
433
|
-
}
|
|
434
|
-
]
|
|
435
|
-
},
|
|
436
368
|
"developers": {
|
|
437
369
|
"id": "developers",
|
|
438
370
|
"appDir": "TopoloDevelopers",
|
|
@@ -509,30 +441,6 @@ var APPLICATIONS = {
|
|
|
509
441
|
}
|
|
510
442
|
]
|
|
511
443
|
},
|
|
512
|
-
"flow": {
|
|
513
|
-
"id": "flow",
|
|
514
|
-
"appDir": "TopoloFlow",
|
|
515
|
-
"name": "topolo-flow",
|
|
516
|
-
"packageName": "topolo-flow",
|
|
517
|
-
"productionUrl": "https://flow.topolo.app",
|
|
518
|
-
"services": [
|
|
519
|
-
"flow"
|
|
520
|
-
],
|
|
521
|
-
"deployTargets": [
|
|
522
|
-
{
|
|
523
|
-
"name": "topolo-flow",
|
|
524
|
-
"kind": "worker",
|
|
525
|
-
"apiUrl": "https://flow.topolo.app",
|
|
526
|
-
"domains": [
|
|
527
|
-
"flow.topolo.app"
|
|
528
|
-
],
|
|
529
|
-
"routes": [
|
|
530
|
-
"flow.topolo.app"
|
|
531
|
-
],
|
|
532
|
-
"serviceId": "flow"
|
|
533
|
-
}
|
|
534
|
-
]
|
|
535
|
-
},
|
|
536
444
|
"forecast": {
|
|
537
445
|
"id": "forecast",
|
|
538
446
|
"appDir": "TopoloForecast",
|
|
@@ -561,78 +469,6 @@ var APPLICATIONS = {
|
|
|
561
469
|
}
|
|
562
470
|
]
|
|
563
471
|
},
|
|
564
|
-
"forms": {
|
|
565
|
-
"id": "forms",
|
|
566
|
-
"appDir": "TopoloForms",
|
|
567
|
-
"name": "topolo-forms",
|
|
568
|
-
"packageName": "topolo-forms",
|
|
569
|
-
"productionUrl": "https://forms.topolo.app",
|
|
570
|
-
"services": [
|
|
571
|
-
"forms"
|
|
572
|
-
],
|
|
573
|
-
"deployTargets": [
|
|
574
|
-
{
|
|
575
|
-
"name": "topolo-forms",
|
|
576
|
-
"kind": "worker",
|
|
577
|
-
"apiUrl": "https://forms.topolo.app",
|
|
578
|
-
"domains": [
|
|
579
|
-
"forms.topolo.app"
|
|
580
|
-
],
|
|
581
|
-
"routes": [
|
|
582
|
-
"forms.topolo.app"
|
|
583
|
-
],
|
|
584
|
-
"serviceId": "forms"
|
|
585
|
-
}
|
|
586
|
-
]
|
|
587
|
-
},
|
|
588
|
-
"insights": {
|
|
589
|
-
"id": "insights",
|
|
590
|
-
"appDir": "TopoloInsights",
|
|
591
|
-
"name": "topolo-insights",
|
|
592
|
-
"packageName": "topolo-insights",
|
|
593
|
-
"productionUrl": "https://insights.topolo.app",
|
|
594
|
-
"services": [
|
|
595
|
-
"insights"
|
|
596
|
-
],
|
|
597
|
-
"deployTargets": [
|
|
598
|
-
{
|
|
599
|
-
"name": "topolo-insights",
|
|
600
|
-
"kind": "worker",
|
|
601
|
-
"apiUrl": "https://insights.topolo.app",
|
|
602
|
-
"domains": [
|
|
603
|
-
"insights.topolo.app"
|
|
604
|
-
],
|
|
605
|
-
"routes": [
|
|
606
|
-
"insights.topolo.app"
|
|
607
|
-
],
|
|
608
|
-
"serviceId": "insights"
|
|
609
|
-
}
|
|
610
|
-
]
|
|
611
|
-
},
|
|
612
|
-
"inventory": {
|
|
613
|
-
"id": "inventory",
|
|
614
|
-
"appDir": "TopoloInventory",
|
|
615
|
-
"name": "topolo-inventory",
|
|
616
|
-
"packageName": "topolo-inventory",
|
|
617
|
-
"productionUrl": "https://inventory.topolo.app",
|
|
618
|
-
"services": [
|
|
619
|
-
"inventory"
|
|
620
|
-
],
|
|
621
|
-
"deployTargets": [
|
|
622
|
-
{
|
|
623
|
-
"name": "topolo-inventory",
|
|
624
|
-
"kind": "worker",
|
|
625
|
-
"apiUrl": "https://inventory.topolo.app",
|
|
626
|
-
"domains": [
|
|
627
|
-
"inventory.topolo.app"
|
|
628
|
-
],
|
|
629
|
-
"routes": [
|
|
630
|
-
"inventory.topolo.app"
|
|
631
|
-
],
|
|
632
|
-
"serviceId": "inventory"
|
|
633
|
-
}
|
|
634
|
-
]
|
|
635
|
-
},
|
|
636
472
|
"learn": {
|
|
637
473
|
"id": "learn",
|
|
638
474
|
"appDir": "TopoloLearn",
|
|
@@ -833,9 +669,7 @@ var APPLICATIONS = {
|
|
|
833
669
|
"kind": "worker",
|
|
834
670
|
"apiUrl": null,
|
|
835
671
|
"domains": [
|
|
836
|
-
"one.topolo.io"
|
|
837
|
-
"topolo.io",
|
|
838
|
-
"www.topolo.io"
|
|
672
|
+
"one.topolo.io"
|
|
839
673
|
],
|
|
840
674
|
"routes": [],
|
|
841
675
|
"serviceId": null
|
|
@@ -872,30 +706,6 @@ var APPLICATIONS = {
|
|
|
872
706
|
}
|
|
873
707
|
]
|
|
874
708
|
},
|
|
875
|
-
"people": {
|
|
876
|
-
"id": "people",
|
|
877
|
-
"appDir": "TopoloPeople",
|
|
878
|
-
"name": "topolo-people",
|
|
879
|
-
"packageName": "topolo-people",
|
|
880
|
-
"productionUrl": "https://people.topolo.app",
|
|
881
|
-
"services": [
|
|
882
|
-
"people"
|
|
883
|
-
],
|
|
884
|
-
"deployTargets": [
|
|
885
|
-
{
|
|
886
|
-
"name": "topolo-people",
|
|
887
|
-
"kind": "worker",
|
|
888
|
-
"apiUrl": "https://people.topolo.app",
|
|
889
|
-
"domains": [
|
|
890
|
-
"people.topolo.app"
|
|
891
|
-
],
|
|
892
|
-
"routes": [
|
|
893
|
-
"people.topolo.app"
|
|
894
|
-
],
|
|
895
|
-
"serviceId": "people"
|
|
896
|
-
}
|
|
897
|
-
]
|
|
898
|
-
},
|
|
899
709
|
"quro": {
|
|
900
710
|
"id": "quro",
|
|
901
711
|
"appDir": "TopoloQuro",
|
|
@@ -967,30 +777,6 @@ var APPLICATIONS = {
|
|
|
967
777
|
}
|
|
968
778
|
]
|
|
969
779
|
},
|
|
970
|
-
"sign": {
|
|
971
|
-
"id": "sign",
|
|
972
|
-
"appDir": "TopoloSign",
|
|
973
|
-
"name": "topolo-sign",
|
|
974
|
-
"packageName": "topolo-sign",
|
|
975
|
-
"productionUrl": "https://sign.topolo.app",
|
|
976
|
-
"services": [
|
|
977
|
-
"sign"
|
|
978
|
-
],
|
|
979
|
-
"deployTargets": [
|
|
980
|
-
{
|
|
981
|
-
"name": "topolo-sign",
|
|
982
|
-
"kind": "worker",
|
|
983
|
-
"apiUrl": "https://sign.topolo.app",
|
|
984
|
-
"domains": [
|
|
985
|
-
"sign.topolo.app"
|
|
986
|
-
],
|
|
987
|
-
"routes": [
|
|
988
|
-
"sign.topolo.app"
|
|
989
|
-
],
|
|
990
|
-
"serviceId": "sign"
|
|
991
|
-
}
|
|
992
|
-
]
|
|
993
|
-
},
|
|
994
780
|
"social_studio": {
|
|
995
781
|
"id": "social_studio",
|
|
996
782
|
"appDir": "TopoloSocialStudio",
|
|
@@ -1087,54 +873,6 @@ var APPLICATIONS = {
|
|
|
1087
873
|
}
|
|
1088
874
|
]
|
|
1089
875
|
},
|
|
1090
|
-
"survey": {
|
|
1091
|
-
"id": "survey",
|
|
1092
|
-
"appDir": "TopoloSurvey",
|
|
1093
|
-
"name": "topolo-survey",
|
|
1094
|
-
"packageName": "topolo-survey",
|
|
1095
|
-
"productionUrl": "https://survey.topolo.app",
|
|
1096
|
-
"services": [
|
|
1097
|
-
"survey"
|
|
1098
|
-
],
|
|
1099
|
-
"deployTargets": [
|
|
1100
|
-
{
|
|
1101
|
-
"name": "topolo-survey",
|
|
1102
|
-
"kind": "worker",
|
|
1103
|
-
"apiUrl": "https://survey.topolo.app",
|
|
1104
|
-
"domains": [
|
|
1105
|
-
"survey.topolo.app"
|
|
1106
|
-
],
|
|
1107
|
-
"routes": [
|
|
1108
|
-
"survey.topolo.app"
|
|
1109
|
-
],
|
|
1110
|
-
"serviceId": "survey"
|
|
1111
|
-
}
|
|
1112
|
-
]
|
|
1113
|
-
},
|
|
1114
|
-
"voice": {
|
|
1115
|
-
"id": "voice",
|
|
1116
|
-
"appDir": "TopoloVoice",
|
|
1117
|
-
"name": "topolo-voice",
|
|
1118
|
-
"packageName": "topolo-voice",
|
|
1119
|
-
"productionUrl": "https://voice.topolo.app",
|
|
1120
|
-
"services": [
|
|
1121
|
-
"voice"
|
|
1122
|
-
],
|
|
1123
|
-
"deployTargets": [
|
|
1124
|
-
{
|
|
1125
|
-
"name": "topolo-voice",
|
|
1126
|
-
"kind": "worker",
|
|
1127
|
-
"apiUrl": "https://voice.topolo.app",
|
|
1128
|
-
"domains": [
|
|
1129
|
-
"voice.topolo.app"
|
|
1130
|
-
],
|
|
1131
|
-
"routes": [
|
|
1132
|
-
"voice.topolo.app"
|
|
1133
|
-
],
|
|
1134
|
-
"serviceId": "voice"
|
|
1135
|
-
}
|
|
1136
|
-
]
|
|
1137
|
-
},
|
|
1138
876
|
"web": {
|
|
1139
877
|
"id": "web",
|
|
1140
878
|
"appDir": "TopoloWeb",
|
|
@@ -2204,7 +1942,7 @@ function sleep(ms) {
|
|
|
2204
1942
|
}
|
|
2205
1943
|
|
|
2206
1944
|
// src/version.ts
|
|
2207
|
-
var CLI_VERSION = "0.1.
|
|
1945
|
+
var CLI_VERSION = "0.1.12";
|
|
2208
1946
|
|
|
2209
1947
|
// src/client.ts
|
|
2210
1948
|
async function buildClient(options) {
|
|
@@ -2978,11 +2716,21 @@ var CLOUDFLARE_ACCOUNT_ID = "49ef1ba682ad8cfd720c86699ae17521";
|
|
|
2978
2716
|
var CURRENT_VERIFICATION_DATE = "2026-04-23";
|
|
2979
2717
|
function applicationScaffoldPayload(application, options = {}) {
|
|
2980
2718
|
const target = normalizeTarget(options.target);
|
|
2719
|
+
const portfolio = target === "platform" ? normalizePortfolio(options.portfolio) : null;
|
|
2981
2720
|
const id = normalizeApplicationId(application);
|
|
2982
2721
|
const slug = toSlug(id);
|
|
2983
2722
|
const catalogApplication = APPLICATIONS[id];
|
|
2984
2723
|
const browser = options.browser !== false;
|
|
2985
2724
|
const api = options.api ?? target === "platform";
|
|
2725
|
+
const audience = normalizeAudience(
|
|
2726
|
+
options.audience,
|
|
2727
|
+
target === "platform" ? portfolio : null
|
|
2728
|
+
);
|
|
2729
|
+
const tenancy = normalizeTenancy(options.tenancy, audience);
|
|
2730
|
+
const surfaceModel = normalizeSurfaceModel(options.surfaceModel);
|
|
2731
|
+
const containerAppId = normalizeContainerAppId(options.containerAppId, surfaceModel);
|
|
2732
|
+
const ownerType = target === "platform" ? "topolo_first_party" : "third_party";
|
|
2733
|
+
const distributionModel = target === "platform" ? "marketplace" : "organization_internal";
|
|
2986
2734
|
const appDir = options.appDir ?? catalogApplication?.appDir ?? (target === "platform" ? `Topolo${toPascalCase(id)}` : `topolo-${slug}`);
|
|
2987
2735
|
const name = options.name ?? catalogApplication?.name ?? `topolo-${slug}`;
|
|
2988
2736
|
const packageName = options.packageName ?? catalogApplication?.packageName ?? `topolo-${slug}`;
|
|
@@ -3003,6 +2751,13 @@ function applicationScaffoldPayload(application, options = {}) {
|
|
|
3003
2751
|
return {
|
|
3004
2752
|
version: APPLICATION_REQUIREMENTS_VERSION,
|
|
3005
2753
|
target,
|
|
2754
|
+
ownerType,
|
|
2755
|
+
portfolio,
|
|
2756
|
+
audience,
|
|
2757
|
+
tenancy,
|
|
2758
|
+
surfaceModel,
|
|
2759
|
+
containerAppId,
|
|
2760
|
+
distributionModel,
|
|
3006
2761
|
application: applicationPayload,
|
|
3007
2762
|
scopes,
|
|
3008
2763
|
requirements: APPLICATION_REQUIREMENTS.filter(
|
|
@@ -3010,13 +2765,28 @@ function applicationScaffoldPayload(application, options = {}) {
|
|
|
3010
2765
|
),
|
|
3011
2766
|
packages: scaffoldPackages({ browser, api, target }),
|
|
3012
2767
|
files: scaffoldFiles({
|
|
2768
|
+
payload: {
|
|
2769
|
+
ownerType,
|
|
2770
|
+
portfolio,
|
|
2771
|
+
audience,
|
|
2772
|
+
tenancy,
|
|
2773
|
+
surfaceModel,
|
|
2774
|
+
containerAppId,
|
|
2775
|
+
distributionModel
|
|
2776
|
+
},
|
|
3013
2777
|
application: applicationPayload,
|
|
3014
2778
|
target,
|
|
3015
2779
|
browser,
|
|
3016
2780
|
api,
|
|
3017
2781
|
ciWorkflow
|
|
3018
2782
|
}),
|
|
3019
|
-
commands: scaffoldCommands(
|
|
2783
|
+
commands: scaffoldCommands({
|
|
2784
|
+
id,
|
|
2785
|
+
target,
|
|
2786
|
+
audience,
|
|
2787
|
+
tenancy,
|
|
2788
|
+
portfolio
|
|
2789
|
+
}),
|
|
3020
2790
|
ciWorkflowPath,
|
|
3021
2791
|
ciWorkflow
|
|
3022
2792
|
};
|
|
@@ -3098,7 +2868,12 @@ function scaffoldFiles(options) {
|
|
|
3098
2868
|
"documents the generated app target",
|
|
3099
2869
|
"points maintainers to the shared Topolo packages used by the scaffold"
|
|
3100
2870
|
],
|
|
3101
|
-
content: readme(application, target, {
|
|
2871
|
+
content: readme(application, target, {
|
|
2872
|
+
api: options.api,
|
|
2873
|
+
audience: options.payload.audience,
|
|
2874
|
+
tenancy: options.payload.tenancy,
|
|
2875
|
+
portfolio: options.payload.portfolio
|
|
2876
|
+
})
|
|
3102
2877
|
},
|
|
3103
2878
|
{
|
|
3104
2879
|
path: `${base}/.gitignore`,
|
|
@@ -3194,7 +2969,12 @@ function scaffoldFiles(options) {
|
|
|
3194
2969
|
"doc_paths includes internal handbook coverage",
|
|
3195
2970
|
"security_assurance records the auth, tenancy, deployment, and verification boundary"
|
|
3196
2971
|
],
|
|
3197
|
-
content: systemJson(application, {
|
|
2972
|
+
content: systemJson(application, {
|
|
2973
|
+
api: options.api,
|
|
2974
|
+
audience: options.payload.audience,
|
|
2975
|
+
tenancy: options.payload.tenancy,
|
|
2976
|
+
portfolio: options.payload.portfolio
|
|
2977
|
+
})
|
|
3198
2978
|
},
|
|
3199
2979
|
{
|
|
3200
2980
|
path: `PlatformApplications/TopoloDocs/src/content/internal/apps/${slug}.mdx`,
|
|
@@ -3203,7 +2983,12 @@ function scaffoldFiles(options) {
|
|
|
3203
2983
|
"documents ownership, routes, auth boundary, deployment, operations, and verification",
|
|
3204
2984
|
"last_verified is updated when behavior changes"
|
|
3205
2985
|
],
|
|
3206
|
-
content: internalDocs(application, {
|
|
2986
|
+
content: internalDocs(application, {
|
|
2987
|
+
api: options.api,
|
|
2988
|
+
audience: options.payload.audience,
|
|
2989
|
+
tenancy: options.payload.tenancy,
|
|
2990
|
+
portfolio: options.payload.portfolio
|
|
2991
|
+
})
|
|
3207
2992
|
},
|
|
3208
2993
|
{
|
|
3209
2994
|
path: `PlatformApplications/TopoloDocs/src/content/public/applications/${slug}.mdx`,
|
|
@@ -3212,7 +2997,12 @@ function scaffoldFiles(options) {
|
|
|
3212
2997
|
"documents the product surface without internal-only details",
|
|
3213
2998
|
"links back to the canonical system registry entry"
|
|
3214
2999
|
],
|
|
3215
|
-
content: publicDocs(application, {
|
|
3000
|
+
content: publicDocs(application, {
|
|
3001
|
+
api: options.api,
|
|
3002
|
+
audience: options.payload.audience,
|
|
3003
|
+
tenancy: options.payload.tenancy,
|
|
3004
|
+
portfolio: options.payload.portfolio
|
|
3005
|
+
})
|
|
3216
3006
|
}
|
|
3217
3007
|
);
|
|
3218
3008
|
if (options.api && application.serviceId) {
|
|
@@ -3224,7 +3014,9 @@ function scaffoldFiles(options) {
|
|
|
3224
3014
|
"permissions, role bundles, and API-key scopes exist before the app is catalog-visible",
|
|
3225
3015
|
"systems metadata maps the service back to the generated TopoloDocs entries"
|
|
3226
3016
|
],
|
|
3227
|
-
content: serviceManifestJson(application
|
|
3017
|
+
content: serviceManifestJson(application, {
|
|
3018
|
+
tenancy: options.payload.tenancy
|
|
3019
|
+
})
|
|
3228
3020
|
});
|
|
3229
3021
|
}
|
|
3230
3022
|
} else {
|
|
@@ -3236,7 +3028,14 @@ function scaffoldFiles(options) {
|
|
|
3236
3028
|
"declares the external app id, callback URLs, and requested platform scopes",
|
|
3237
3029
|
"does not include internal Topolo CloudControl or canonical-docs ownership"
|
|
3238
3030
|
],
|
|
3239
|
-
content: developerManifest(application, {
|
|
3031
|
+
content: developerManifest(application, {
|
|
3032
|
+
api: options.api,
|
|
3033
|
+
audience: options.payload.audience,
|
|
3034
|
+
tenancy: options.payload.tenancy,
|
|
3035
|
+
surfaceModel: options.payload.surfaceModel,
|
|
3036
|
+
containerAppId: options.payload.containerAppId,
|
|
3037
|
+
distributionModel: options.payload.distributionModel
|
|
3038
|
+
})
|
|
3240
3039
|
},
|
|
3241
3040
|
{
|
|
3242
3041
|
path: `${base}/.env.example`,
|
|
@@ -3259,7 +3058,10 @@ function scaffoldFiles(options) {
|
|
|
3259
3058
|
"keeps launcher and account-menu behavior on the shared platform surface",
|
|
3260
3059
|
"passes app-specific account actions through accountMenuItems or TopoloAccountMenu"
|
|
3261
3060
|
],
|
|
3262
|
-
content: appTsx(application, target
|
|
3061
|
+
content: appTsx(application, target, {
|
|
3062
|
+
audience: options.payload.audience,
|
|
3063
|
+
portfolio: options.payload.portfolio
|
|
3064
|
+
})
|
|
3263
3065
|
});
|
|
3264
3066
|
}
|
|
3265
3067
|
if (options.api) {
|
|
@@ -3281,28 +3083,33 @@ function scaffoldFiles(options) {
|
|
|
3281
3083
|
"derives organization from the credential rather than client-supplied orgId",
|
|
3282
3084
|
"emits or propagates X-Topolo-Request-Id and X-Topolo-Client"
|
|
3283
3085
|
],
|
|
3284
|
-
content: workerTs(application, target
|
|
3086
|
+
content: workerTs(application, target, {
|
|
3087
|
+
audience: options.payload.audience,
|
|
3088
|
+
tenancy: options.payload.tenancy
|
|
3089
|
+
})
|
|
3285
3090
|
}
|
|
3286
3091
|
);
|
|
3287
3092
|
}
|
|
3288
3093
|
return files;
|
|
3289
3094
|
}
|
|
3290
|
-
function scaffoldCommands(
|
|
3291
|
-
const scaffoldId = toSlug(id);
|
|
3292
|
-
if (target === "developer") {
|
|
3095
|
+
function scaffoldCommands(options) {
|
|
3096
|
+
const scaffoldId = toSlug(options.id);
|
|
3097
|
+
if (options.target === "developer") {
|
|
3098
|
+
const audienceFlags = options.audience === "personal" ? " --audience personal --tenancy household" : "";
|
|
3293
3099
|
return [
|
|
3294
|
-
`topolo apps scaffold ${scaffoldId} --
|
|
3100
|
+
`topolo apps scaffold ${scaffoldId}${audienceFlags} --write --repo-root .`,
|
|
3295
3101
|
`cd topolo-${scaffoldId}`,
|
|
3296
3102
|
"npm install",
|
|
3297
3103
|
"npm run typecheck",
|
|
3298
3104
|
"npm run build"
|
|
3299
3105
|
];
|
|
3300
3106
|
}
|
|
3107
|
+
const portfolioFlags = options.portfolio === "personal" ? " --portfolio personal" : "";
|
|
3301
3108
|
return [
|
|
3302
|
-
`topolo apps scaffold ${scaffoldId} --write --repo-root /Users/ashes/Projects/topolo --json`,
|
|
3109
|
+
`topolo platform apps scaffold ${scaffoldId}${portfolioFlags} --write --repo-root /Users/ashes/Projects/topolo --json`,
|
|
3303
3110
|
"topolo apps requirements --json",
|
|
3304
|
-
`topolo apps audit ${id} --local --repo-root /Users/ashes/Projects/topolo --fail-on needs_review --json`,
|
|
3305
|
-
`topolo apps audit ${id} --local --repo-root /Users/ashes/Projects/topolo --fail-on partial --json`
|
|
3111
|
+
`topolo apps audit ${options.id} --local --repo-root /Users/ashes/Projects/topolo --fail-on needs_review --json`,
|
|
3112
|
+
`topolo apps audit ${options.id} --local --repo-root /Users/ashes/Projects/topolo --fail-on partial --json`
|
|
3306
3113
|
];
|
|
3307
3114
|
}
|
|
3308
3115
|
function topoloConformanceWorkflow(options) {
|
|
@@ -3430,12 +3237,16 @@ function topoloPackageVersion(packageName, target) {
|
|
|
3430
3237
|
}
|
|
3431
3238
|
function readme(application, target, options) {
|
|
3432
3239
|
const displayName = toTitle(application.name);
|
|
3433
|
-
const targetLabel = target === "platform" ? "first-party platform application" : "third-party Topolo application";
|
|
3240
|
+
const targetLabel = target === "platform" ? options.portfolio === "personal" ? "first-party Topolo personal application" : "first-party Topolo platform application" : options.audience === "personal" ? "third-party Topolo personal application" : "third-party Topolo business application";
|
|
3434
3241
|
const lines = [
|
|
3435
3242
|
`# ${displayName}`,
|
|
3436
3243
|
"",
|
|
3437
3244
|
`Generated ${targetLabel} scaffold for Topolo.`,
|
|
3438
3245
|
"",
|
|
3246
|
+
`Audience: ${options.audience}`,
|
|
3247
|
+
`Tenancy: ${options.tenancy}`,
|
|
3248
|
+
...target === "platform" && options.portfolio ? [`Portfolio: ${options.portfolio}`] : [],
|
|
3249
|
+
"",
|
|
3439
3250
|
"## Development",
|
|
3440
3251
|
"",
|
|
3441
3252
|
"```bash",
|
|
@@ -3464,6 +3275,7 @@ function readme(application, target, options) {
|
|
|
3464
3275
|
"",
|
|
3465
3276
|
"- `topolo.app.json` declares callback URLs, requested scopes, and runtime shape for Topolo Developers.",
|
|
3466
3277
|
"- Browser sign-in uses the public Topolo OAuth authorization-code + PKCE flow.",
|
|
3278
|
+
`- The generated app is seeded as a ${options.audience}/${options.tenancy} developer application.`,
|
|
3467
3279
|
"- The starter intentionally avoids private first-party packages so it can install outside the Topolo monorepo."
|
|
3468
3280
|
);
|
|
3469
3281
|
}
|
|
@@ -3651,13 +3463,15 @@ function stylesCss() {
|
|
|
3651
3463
|
""
|
|
3652
3464
|
].join("\n");
|
|
3653
3465
|
}
|
|
3654
|
-
function appTsx(application, target) {
|
|
3466
|
+
function appTsx(application, target, options) {
|
|
3655
3467
|
if (target === "developer") return developerAppTsx(application);
|
|
3656
|
-
return platformAppTsx(application);
|
|
3468
|
+
return platformAppTsx(application, options);
|
|
3657
3469
|
}
|
|
3658
|
-
function platformAppTsx(application) {
|
|
3470
|
+
function platformAppTsx(application, options) {
|
|
3659
3471
|
const appName = toTitle(application.name);
|
|
3660
3472
|
const serviceId = application.serviceId ?? application.id;
|
|
3473
|
+
const tagline = options.audience === "personal" ? "Topolo personal app" : "Topolo workspace";
|
|
3474
|
+
const workspaceTitle = options.audience === "personal" ? "Personal app" : "Workspace";
|
|
3661
3475
|
return [
|
|
3662
3476
|
"import { useEffect, useMemo, useState } from 'react';",
|
|
3663
3477
|
"import { createBrowserAuthModule, type AuthUser } from '@topolo/auth-client';",
|
|
@@ -3673,7 +3487,7 @@ function platformAppTsx(application) {
|
|
|
3673
3487
|
"const SERVICE_ID = import.meta.env.VITE_TOPOLO_SERVICE_ID ?? DEFAULT_SERVICE_ID;",
|
|
3674
3488
|
"const LANDING_CONFIG = {",
|
|
3675
3489
|
" app_name: APP_NAME,",
|
|
3676
|
-
|
|
3490
|
+
` app_tagline: '${escapeSingleQuoted(tagline)}',`,
|
|
3677
3491
|
" hero_title: `Open ${APP_NAME} in Topolo`,",
|
|
3678
3492
|
" hero_subtitle: 'Shared landing, login, callback, auth, and workspace shell wiring are already included in the generated baseline.',",
|
|
3679
3493
|
" hero_cta_text: 'Sign in',",
|
|
@@ -3683,7 +3497,7 @@ function platformAppTsx(application) {
|
|
|
3683
3497
|
"};",
|
|
3684
3498
|
"const LOGIN_CONFIG = {",
|
|
3685
3499
|
" app_name: APP_NAME,",
|
|
3686
|
-
|
|
3500
|
+
` app_tagline: '${escapeSingleQuoted(tagline)}',`,
|
|
3687
3501
|
" hero_title: `Sign in to ${APP_NAME}`,",
|
|
3688
3502
|
" hero_subtitle: 'Use Topolo Auth to open the shared workspace shell for this application.',",
|
|
3689
3503
|
" authenticated_home_path: '/',",
|
|
@@ -3857,10 +3671,10 @@ function platformAppTsx(application) {
|
|
|
3857
3671
|
" appName={APP_NAME}",
|
|
3858
3672
|
" user={shellUser}",
|
|
3859
3673
|
' activeNavItemId="home"',
|
|
3860
|
-
|
|
3674
|
+
` navItems={[{ id: 'home', label: '${escapeSingleQuoted(workspaceTitle)}' }]}`,
|
|
3861
3675
|
" onNavItemSelect={() => undefined}",
|
|
3862
3676
|
" eyebrow={APP_NAME}",
|
|
3863
|
-
|
|
3677
|
+
` title="${escapeDoubleQuoted(workspaceTitle)}"`,
|
|
3864
3678
|
" authUrl={AUTH_URL}",
|
|
3865
3679
|
" getAccessToken={getAccessToken}",
|
|
3866
3680
|
" onSignOut={async () => {",
|
|
@@ -4076,7 +3890,7 @@ function wranglerToml(application, target) {
|
|
|
4076
3890
|
);
|
|
4077
3891
|
return lines.join("\n");
|
|
4078
3892
|
}
|
|
4079
|
-
function workerTs(application, target) {
|
|
3893
|
+
function workerTs(application, target, _options) {
|
|
4080
3894
|
if (target === "developer") return developerWorkerTs(application);
|
|
4081
3895
|
return platformWorkerTs(application);
|
|
4082
3896
|
}
|
|
@@ -4281,12 +4095,14 @@ function cloudcontrolJson(application, options) {
|
|
|
4281
4095
|
}
|
|
4282
4096
|
function systemJson(application, options) {
|
|
4283
4097
|
const slug = toSlug(application.id);
|
|
4098
|
+
const suiteLabel = options.audience === "personal" ? "personal" : "platform";
|
|
4099
|
+
const summaryLabel = options.audience === "personal" ? "first-party Topolo personal application" : "first-party Topolo platform application";
|
|
4284
4100
|
return `${json({
|
|
4285
4101
|
id: `topolo-${slug}`,
|
|
4286
4102
|
name: toTitle(application.name),
|
|
4287
4103
|
slug: `topolo-${slug}`,
|
|
4288
4104
|
kind: "application",
|
|
4289
|
-
summary: `${toTitle(application.name)} is a
|
|
4105
|
+
summary: `${toTitle(application.name)} is a ${summaryLabel} scaffolded on shared Auth, UI Kit, CloudControl, and conformance contracts.`,
|
|
4290
4106
|
lifecycle: "planned",
|
|
4291
4107
|
last_verified: CURRENT_VERIFICATION_DATE,
|
|
4292
4108
|
owners: ["platform-engineering"],
|
|
@@ -4303,7 +4119,7 @@ function systemJson(application, options) {
|
|
|
4303
4119
|
security_assurance: {
|
|
4304
4120
|
risk_tier: "internal",
|
|
4305
4121
|
auth_boundary: "Topolo Auth through @topolo/auth-client for browser sessions and @topolo/auth-middleware for Worker requests.",
|
|
4306
|
-
tenant_isolation: "organization_scoped",
|
|
4122
|
+
tenant_isolation: options.tenancy === "household" ? "household_scoped" : "organization_scoped",
|
|
4307
4123
|
external_inputs: ["browser", ...options.api ? ["api"] : []],
|
|
4308
4124
|
sensitive_data: ["identity", "org_data"],
|
|
4309
4125
|
security_review_status: "not_started",
|
|
@@ -4311,13 +4127,16 @@ function systemJson(application, options) {
|
|
|
4311
4127
|
pentest_status: "not_started",
|
|
4312
4128
|
evidence_doc: `internal/apps/${slug}`
|
|
4313
4129
|
},
|
|
4314
|
-
dependencies: ["topolo-auth", "topolo-platform"]
|
|
4130
|
+
dependencies: options.audience === "personal" ? ["topolo-auth"] : ["topolo-auth", "topolo-platform"],
|
|
4131
|
+
tags: ["application", "cloudflare", "typescript", suiteLabel]
|
|
4315
4132
|
})}
|
|
4316
4133
|
`;
|
|
4317
4134
|
}
|
|
4318
4135
|
function internalDocs(application, options) {
|
|
4319
4136
|
const slug = toSlug(application.id);
|
|
4320
4137
|
const title = toTitle(application.name);
|
|
4138
|
+
const suiteLabel = options.audience === "personal" ? "personal" : "platform";
|
|
4139
|
+
const tenantLabel = options.tenancy === "household" ? "household" : "organization";
|
|
4321
4140
|
return [
|
|
4322
4141
|
"---",
|
|
4323
4142
|
`title: "${escapeFrontmatter(title)} Handbook"`,
|
|
@@ -4342,7 +4161,7 @@ function internalDocs(application, options) {
|
|
|
4342
4161
|
"",
|
|
4343
4162
|
"## What It Is",
|
|
4344
4163
|
"",
|
|
4345
|
-
`${title} is a first-party Topolo application scaffolded from the shared
|
|
4164
|
+
`${title} is a first-party Topolo ${suiteLabel} application scaffolded from the shared first-party application contract.`,
|
|
4346
4165
|
"",
|
|
4347
4166
|
"## Architecture",
|
|
4348
4167
|
"",
|
|
@@ -4368,7 +4187,7 @@ function internalDocs(application, options) {
|
|
|
4368
4187
|
"",
|
|
4369
4188
|
"## Data Ownership",
|
|
4370
4189
|
"",
|
|
4371
|
-
|
|
4190
|
+
`The generated scaffold stores no durable application data yet. Future domain tables, Durable Objects, queues, KV namespaces, R2 buckets, and external provider records must be scoped to the authenticated ${tenantLabel} context at the backend boundary.`,
|
|
4372
4191
|
"",
|
|
4373
4192
|
"## Deployments",
|
|
4374
4193
|
"",
|
|
@@ -4390,7 +4209,7 @@ function internalDocs(application, options) {
|
|
|
4390
4209
|
"",
|
|
4391
4210
|
"## Change Log / Verification",
|
|
4392
4211
|
"",
|
|
4393
|
-
`- Scaffold generated on ${CURRENT_VERIFICATION_DATE} from
|
|
4212
|
+
`- Scaffold generated on ${CURRENT_VERIFICATION_DATE} from \`${options.portfolio ? "topolo platform apps scaffold" : "topolo apps scaffold"}\`.`,
|
|
4394
4213
|
"- `npm run typecheck`",
|
|
4395
4214
|
"- `npm run build`",
|
|
4396
4215
|
"- `npm run validate` in TopoloDocs",
|
|
@@ -4400,6 +4219,8 @@ function internalDocs(application, options) {
|
|
|
4400
4219
|
function publicDocs(application, options) {
|
|
4401
4220
|
const slug = toSlug(application.id);
|
|
4402
4221
|
const title = toTitle(application.name);
|
|
4222
|
+
const suiteLabel = options.audience === "personal" ? "personal" : "business";
|
|
4223
|
+
const tenantLabel = options.tenancy === "household" ? "household" : "organization";
|
|
4403
4224
|
return [
|
|
4404
4225
|
"---",
|
|
4405
4226
|
`title: "${escapeFrontmatter(title)}"`,
|
|
@@ -4423,7 +4244,7 @@ function publicDocs(application, options) {
|
|
|
4423
4244
|
"",
|
|
4424
4245
|
"## What It Is",
|
|
4425
4246
|
"",
|
|
4426
|
-
`${title} is part of the Topolo
|
|
4247
|
+
`${title} is part of the Topolo ${suiteLabel} application suite.`,
|
|
4427
4248
|
"",
|
|
4428
4249
|
"## Architecture",
|
|
4429
4250
|
"",
|
|
@@ -4443,7 +4264,7 @@ function publicDocs(application, options) {
|
|
|
4443
4264
|
"",
|
|
4444
4265
|
"## Data Ownership",
|
|
4445
4266
|
"",
|
|
4446
|
-
|
|
4267
|
+
`The generated baseline has no durable domain data yet. Future data must be ${tenantLabel}-scoped at the backend boundary.`,
|
|
4447
4268
|
"",
|
|
4448
4269
|
"## Deployments",
|
|
4449
4270
|
"",
|
|
@@ -4465,10 +4286,11 @@ function publicDocs(application, options) {
|
|
|
4465
4286
|
""
|
|
4466
4287
|
].join("\n");
|
|
4467
4288
|
}
|
|
4468
|
-
function serviceManifestJson(application) {
|
|
4289
|
+
function serviceManifestJson(application, options) {
|
|
4469
4290
|
const slug = toSlug(application.id);
|
|
4470
4291
|
const title = toTitle(application.name);
|
|
4471
4292
|
const permissionPrefix = application.id.replace(/[^a-z0-9_]/g, "_");
|
|
4293
|
+
const resourceLabel = options.tenancy === "household" ? "household" : "workspace";
|
|
4472
4294
|
return `${json({
|
|
4473
4295
|
serviceId: application.serviceId,
|
|
4474
4296
|
systems: [
|
|
@@ -4483,13 +4305,13 @@ function serviceManifestJson(application) {
|
|
|
4483
4305
|
{
|
|
4484
4306
|
id: `perm_${permissionPrefix}_workspace_read`,
|
|
4485
4307
|
name: "workspace:read",
|
|
4486
|
-
description: `View ${title}
|
|
4308
|
+
description: `View ${title} ${resourceLabel} data`,
|
|
4487
4309
|
resourcePattern: null
|
|
4488
4310
|
},
|
|
4489
4311
|
{
|
|
4490
4312
|
id: `perm_${permissionPrefix}_workspace_write`,
|
|
4491
4313
|
name: "workspace:write",
|
|
4492
|
-
description: `Manage ${title}
|
|
4314
|
+
description: `Manage ${title} ${resourceLabel} data`,
|
|
4493
4315
|
resourcePattern: null
|
|
4494
4316
|
}
|
|
4495
4317
|
],
|
|
@@ -4497,13 +4319,13 @@ function serviceManifestJson(application) {
|
|
|
4497
4319
|
{
|
|
4498
4320
|
id: `aks_${permissionPrefix}_workspace_read`,
|
|
4499
4321
|
name: "workspace.read",
|
|
4500
|
-
description: `View ${title}
|
|
4322
|
+
description: `View ${title} ${resourceLabel} data`,
|
|
4501
4323
|
resourcePattern: null
|
|
4502
4324
|
},
|
|
4503
4325
|
{
|
|
4504
4326
|
id: `aks_${permissionPrefix}_workspace_write`,
|
|
4505
4327
|
name: "workspace.write",
|
|
4506
|
-
description: `Manage ${title}
|
|
4328
|
+
description: `Manage ${title} ${resourceLabel} data`,
|
|
4507
4329
|
resourcePattern: null
|
|
4508
4330
|
}
|
|
4509
4331
|
],
|
|
@@ -4522,6 +4344,11 @@ function developerManifest(application, options) {
|
|
|
4522
4344
|
id: application.id,
|
|
4523
4345
|
name: toTitle(application.name),
|
|
4524
4346
|
type: "third_party",
|
|
4347
|
+
audience: options.audience,
|
|
4348
|
+
tenancy: options.tenancy,
|
|
4349
|
+
distribution_model: options.distributionModel,
|
|
4350
|
+
surface_model: options.surfaceModel,
|
|
4351
|
+
container_app_id: options.containerAppId,
|
|
4525
4352
|
package_name: application.packageName,
|
|
4526
4353
|
service_id: options.api ? application.serviceId : null,
|
|
4527
4354
|
auth: {
|
|
@@ -4554,6 +4381,52 @@ function normalizeTarget(value) {
|
|
|
4554
4381
|
if (value === "platform" || value === "developer") return value;
|
|
4555
4382
|
throw new Error("Invalid scaffold target. Use platform or developer.");
|
|
4556
4383
|
}
|
|
4384
|
+
function normalizePortfolio(value) {
|
|
4385
|
+
if (!value) return "platform";
|
|
4386
|
+
if (value === "platform" || value === "personal") return value;
|
|
4387
|
+
throw new Error("Invalid scaffold portfolio. Use platform or personal.");
|
|
4388
|
+
}
|
|
4389
|
+
function normalizeAudience(value, portfolio) {
|
|
4390
|
+
if (!value) {
|
|
4391
|
+
return portfolio === "personal" ? "personal" : "business";
|
|
4392
|
+
}
|
|
4393
|
+
if (value === "business" || value === "personal") {
|
|
4394
|
+
return value;
|
|
4395
|
+
}
|
|
4396
|
+
throw new Error("Invalid scaffold audience. Use business or personal.");
|
|
4397
|
+
}
|
|
4398
|
+
function normalizeTenancy(value, audience) {
|
|
4399
|
+
const resolved = value ?? (audience === "personal" ? "household" : "organization");
|
|
4400
|
+
if (resolved !== "organization" && resolved !== "household") {
|
|
4401
|
+
throw new Error("Invalid scaffold tenancy. Use organization or household.");
|
|
4402
|
+
}
|
|
4403
|
+
if (audience === "business" && resolved !== "organization") {
|
|
4404
|
+
throw new Error("Business scaffolds must use organization tenancy.");
|
|
4405
|
+
}
|
|
4406
|
+
if (audience === "personal" && resolved !== "household") {
|
|
4407
|
+
throw new Error("Personal scaffolds must use household tenancy.");
|
|
4408
|
+
}
|
|
4409
|
+
return resolved;
|
|
4410
|
+
}
|
|
4411
|
+
function normalizeSurfaceModel(value) {
|
|
4412
|
+
if (!value) return "web_app";
|
|
4413
|
+
if (value === "web_app" || value === "mobile_container" || value === "mobile_module" || value === "messaging_surface") {
|
|
4414
|
+
return value;
|
|
4415
|
+
}
|
|
4416
|
+
throw new Error(
|
|
4417
|
+
"Invalid scaffold surface model. Use web_app, mobile_container, mobile_module, or messaging_surface."
|
|
4418
|
+
);
|
|
4419
|
+
}
|
|
4420
|
+
function normalizeContainerAppId(value, surfaceModel) {
|
|
4421
|
+
const trimmed = String(value || "").trim();
|
|
4422
|
+
if (surfaceModel === "mobile_module") {
|
|
4423
|
+
if (!trimmed) {
|
|
4424
|
+
throw new Error("Mobile-module scaffolds require --container-app-id.");
|
|
4425
|
+
}
|
|
4426
|
+
return trimmed;
|
|
4427
|
+
}
|
|
4428
|
+
return trimmed || null;
|
|
4429
|
+
}
|
|
4557
4430
|
function normalizeApplicationId(application) {
|
|
4558
4431
|
const raw = application.trim().toLowerCase();
|
|
4559
4432
|
const normalized = raw.replace(/-/g, "_");
|
|
@@ -4604,10 +4477,157 @@ function escapeHtml(value) {
|
|
|
4604
4477
|
function escapeSingleQuoted(value) {
|
|
4605
4478
|
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
4606
4479
|
}
|
|
4480
|
+
function escapeDoubleQuoted(value) {
|
|
4481
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
4482
|
+
}
|
|
4607
4483
|
function escapeFrontmatter(value) {
|
|
4608
4484
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
4609
4485
|
}
|
|
4610
4486
|
|
|
4487
|
+
// src/commands/app-scaffold-provision.ts
|
|
4488
|
+
var DEFAULT_DEVELOPERS_ORIGIN = "https://developers.topolo.app";
|
|
4489
|
+
var DEVELOPERS_SERVICE_ID = "svc_topolo_developers";
|
|
4490
|
+
async function provisionApplicationScaffold(payload) {
|
|
4491
|
+
if (!payload.application.productionUrl) {
|
|
4492
|
+
throw new Error(
|
|
4493
|
+
"Scaffold provisioning requires a browser-capable application with a production URL."
|
|
4494
|
+
);
|
|
4495
|
+
}
|
|
4496
|
+
if (payload.target === "platform" && !payload.application.serviceId) {
|
|
4497
|
+
throw new Error(
|
|
4498
|
+
"Platform scaffold provisioning requires a service id. Re-run without --no-api or pass --service."
|
|
4499
|
+
);
|
|
4500
|
+
}
|
|
4501
|
+
const credential = await refreshCredentialIfNeeded();
|
|
4502
|
+
if (!credential) {
|
|
4503
|
+
throw new Error(
|
|
4504
|
+
"topolo apps scaffold --provision requires an authenticated Topolo session. Run `topolo auth login` first."
|
|
4505
|
+
);
|
|
4506
|
+
}
|
|
4507
|
+
const client = await buildClient();
|
|
4508
|
+
if (!client) {
|
|
4509
|
+
throw new Error(
|
|
4510
|
+
"Could not build a Topolo client for scaffold provisioning. Run `topolo auth login` first."
|
|
4511
|
+
);
|
|
4512
|
+
}
|
|
4513
|
+
const identity = await client.client.introspect();
|
|
4514
|
+
const config = await readConfig();
|
|
4515
|
+
const serviceUrls = resolveServiceUrls(config.serviceUrls) ?? {};
|
|
4516
|
+
const developersOrigin = sanitizeOrigin(serviceUrls.developers ?? DEFAULT_DEVELOPERS_ORIGIN);
|
|
4517
|
+
const endpoint = payload.target === "platform" ? "/api/developer-console/admin/first-party-app-scaffolds" : "/api/developer-console/apps/scaffold";
|
|
4518
|
+
const headers = new Headers({
|
|
4519
|
+
"Content-Type": "application/json",
|
|
4520
|
+
"X-Service-ID": DEVELOPERS_SERVICE_ID,
|
|
4521
|
+
"X-Topolo-Client": `topolo-cli/${CLI_VERSION}`
|
|
4522
|
+
});
|
|
4523
|
+
const agentName = resolveAgentName(config.agentName);
|
|
4524
|
+
if (agentName) {
|
|
4525
|
+
headers.set("X-Topolo-Agent", agentName);
|
|
4526
|
+
}
|
|
4527
|
+
if (credential.kind === "api_key") {
|
|
4528
|
+
headers.set("X-Api-Key", credential.apiKey);
|
|
4529
|
+
} else {
|
|
4530
|
+
headers.set("Authorization", `Bearer ${credential.accessToken}`);
|
|
4531
|
+
}
|
|
4532
|
+
const response = await fetch(`${developersOrigin}${endpoint}`, {
|
|
4533
|
+
method: "POST",
|
|
4534
|
+
headers,
|
|
4535
|
+
body: JSON.stringify(buildProvisioningRequest(payload))
|
|
4536
|
+
});
|
|
4537
|
+
const envelope = await response.json().catch(() => null);
|
|
4538
|
+
if (!response.ok || !envelope?.success || !envelope.data) {
|
|
4539
|
+
throw new Error(
|
|
4540
|
+
resolveProvisioningError(envelope, response.status, payload.target)
|
|
4541
|
+
);
|
|
4542
|
+
}
|
|
4543
|
+
const app = pickRecord(envelope.data.app);
|
|
4544
|
+
const service = pickRecord(envelope.data.service);
|
|
4545
|
+
const authConfig = pickRecord(envelope.data.authConfig);
|
|
4546
|
+
if (!app) {
|
|
4547
|
+
throw new Error("Developers provisioning did not return an app record.");
|
|
4548
|
+
}
|
|
4549
|
+
const appId = readRequiredString(app.id, "Developers provisioning did not return an app id.");
|
|
4550
|
+
return {
|
|
4551
|
+
target: payload.target,
|
|
4552
|
+
actor: {
|
|
4553
|
+
kind: identity.kind,
|
|
4554
|
+
userId: identity.user.id,
|
|
4555
|
+
email: identity.user.email,
|
|
4556
|
+
role: identity.user.role,
|
|
4557
|
+
organizationId: identity.organization?.id ?? null,
|
|
4558
|
+
organizationSlug: identity.organization?.slug ?? null
|
|
4559
|
+
},
|
|
4560
|
+
developersOrigin,
|
|
4561
|
+
developersApp: {
|
|
4562
|
+
id: appId,
|
|
4563
|
+
slug: readString(app.slug),
|
|
4564
|
+
listingStatus: readString(app.listingStatus),
|
|
4565
|
+
organizationId: readString(app.organizationId)
|
|
4566
|
+
},
|
|
4567
|
+
authService: service ? {
|
|
4568
|
+
id: readRequiredString(
|
|
4569
|
+
service.id,
|
|
4570
|
+
"Auth registration did not return a service id."
|
|
4571
|
+
),
|
|
4572
|
+
status: readString(service.status),
|
|
4573
|
+
baseUrl: readString(service.base_url) ?? readString(service.baseUrl)
|
|
4574
|
+
} : null,
|
|
4575
|
+
authConfig: authConfig ? {
|
|
4576
|
+
login: authConfig.login === true,
|
|
4577
|
+
landing: authConfig.landing === true,
|
|
4578
|
+
appUi: authConfig.appUi === true
|
|
4579
|
+
} : null
|
|
4580
|
+
};
|
|
4581
|
+
}
|
|
4582
|
+
function buildProvisioningRequest(payload) {
|
|
4583
|
+
return {
|
|
4584
|
+
target: payload.target,
|
|
4585
|
+
ownerType: payload.ownerType,
|
|
4586
|
+
portfolio: payload.portfolio,
|
|
4587
|
+
audience: payload.audience,
|
|
4588
|
+
tenancy: payload.tenancy,
|
|
4589
|
+
surfaceModel: payload.surfaceModel,
|
|
4590
|
+
containerAppId: payload.containerAppId,
|
|
4591
|
+
distributionModel: payload.distributionModel,
|
|
4592
|
+
applicationId: payload.application.id,
|
|
4593
|
+
appDir: payload.application.appDir,
|
|
4594
|
+
packageName: payload.application.packageName,
|
|
4595
|
+
name: payload.application.name,
|
|
4596
|
+
productionUrl: payload.application.productionUrl,
|
|
4597
|
+
serviceId: payload.application.serviceId
|
|
4598
|
+
};
|
|
4599
|
+
}
|
|
4600
|
+
function resolveProvisioningError(envelope, status, target) {
|
|
4601
|
+
const message = (typeof envelope?.error === "string" ? envelope.error : envelope?.error?.message) || envelope?.message || `Scaffold provisioning failed with status ${status}.`;
|
|
4602
|
+
if (status === 401 || status === 403) {
|
|
4603
|
+
if (target === "platform") {
|
|
4604
|
+
return `${message} Platform scaffold provisioning requires a Topolo platform-admin session with Developers commerce access.`;
|
|
4605
|
+
}
|
|
4606
|
+
return `${message} Developer scaffold provisioning requires a signed-in Topolo developer workspace session.`;
|
|
4607
|
+
}
|
|
4608
|
+
return message;
|
|
4609
|
+
}
|
|
4610
|
+
function sanitizeOrigin(value) {
|
|
4611
|
+
return value.replace(/\/+$/, "");
|
|
4612
|
+
}
|
|
4613
|
+
function pickRecord(value) {
|
|
4614
|
+
return value && typeof value === "object" ? value : null;
|
|
4615
|
+
}
|
|
4616
|
+
function readString(value) {
|
|
4617
|
+
if (typeof value !== "string") {
|
|
4618
|
+
return null;
|
|
4619
|
+
}
|
|
4620
|
+
const trimmed = value.trim();
|
|
4621
|
+
return trimmed ? trimmed : null;
|
|
4622
|
+
}
|
|
4623
|
+
function readRequiredString(value, message) {
|
|
4624
|
+
const resolved = readString(value);
|
|
4625
|
+
if (!resolved) {
|
|
4626
|
+
throw new Error(message);
|
|
4627
|
+
}
|
|
4628
|
+
return resolved;
|
|
4629
|
+
}
|
|
4630
|
+
|
|
4611
4631
|
// src/commands/apps.ts
|
|
4612
4632
|
var APPLICATION_IDS = Object.keys(APPLICATIONS).sort();
|
|
4613
4633
|
function registerAppsCommand(program2) {
|
|
@@ -4669,14 +4689,16 @@ function registerAppsCommand(program2) {
|
|
|
4669
4689
|
}
|
|
4670
4690
|
});
|
|
4671
4691
|
apps.command("scaffold <application>").description(
|
|
4672
|
-
"Generate
|
|
4673
|
-
).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("--
|
|
4692
|
+
"Generate a third-party Topolo application scaffold for business or personal/household use."
|
|
4693
|
+
).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(
|
|
4694
|
+
"--surface-model <surfaceModel>",
|
|
4695
|
+
"Developer app surface model: web_app, mobile_container, mobile_module, or messaging_surface"
|
|
4696
|
+
).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 API/service files for developer scaffolds").option("--no-api", "Do not include API/service requirements").option("--no-browser", "Do not include browser-shell requirements").option(
|
|
4697
|
+
"--provision",
|
|
4698
|
+
"Provision the generated app into the current Topolo developer workspace using the authenticated session"
|
|
4699
|
+
).action(async (application, opts) => {
|
|
4674
4700
|
const mode = opts.json === false || process.argv.includes("--no-json") ? "pretty" : resolveOutputMode(opts.json);
|
|
4675
4701
|
try {
|
|
4676
|
-
const target = resolveScaffoldTarget(opts.target, opts.developer);
|
|
4677
|
-
if (opts.workflowOnly && opts.write) {
|
|
4678
|
-
throw new Error("--workflow-only cannot be used with --write.");
|
|
4679
|
-
}
|
|
4680
4702
|
const api = resolveScaffoldApiOption(opts.api, opts.withApi);
|
|
4681
4703
|
const payload = applicationScaffoldPayload(application, {
|
|
4682
4704
|
appDir: opts.appDir,
|
|
@@ -4686,14 +4708,27 @@ function registerAppsCommand(program2) {
|
|
|
4686
4708
|
service: opts.service,
|
|
4687
4709
|
api,
|
|
4688
4710
|
browser: opts.browser,
|
|
4689
|
-
target
|
|
4711
|
+
target: "developer",
|
|
4712
|
+
audience: resolveScaffoldAudience(opts.audience),
|
|
4713
|
+
tenancy: resolveScaffoldTenancy(opts.tenancy),
|
|
4714
|
+
surfaceModel: resolveScaffoldSurfaceModel(opts.surfaceModel),
|
|
4715
|
+
containerAppId: opts.containerAppId
|
|
4690
4716
|
});
|
|
4691
|
-
if (opts.
|
|
4692
|
-
throw new Error("--
|
|
4717
|
+
if (opts.provision && !opts.write) {
|
|
4718
|
+
throw new Error("--provision requires --write so remote developer records match written scaffold files.");
|
|
4693
4719
|
}
|
|
4694
4720
|
const writeResult = opts.write ? await writeApplicationScaffold(payload, { repoRoot: opts.repoRoot, force: opts.force }) : null;
|
|
4695
|
-
const
|
|
4696
|
-
|
|
4721
|
+
const provisionResult = opts.provision ? await provisionApplicationScaffold(payload) : null;
|
|
4722
|
+
const output = {
|
|
4723
|
+
...payload,
|
|
4724
|
+
...writeResult ? { write: writeResult } : {},
|
|
4725
|
+
...provisionResult ? { provision: provisionResult } : {}
|
|
4726
|
+
};
|
|
4727
|
+
emit(
|
|
4728
|
+
output,
|
|
4729
|
+
mode,
|
|
4730
|
+
() => formatScaffold(payload, writeResult, provisionResult)
|
|
4731
|
+
);
|
|
4697
4732
|
} catch (err) {
|
|
4698
4733
|
fail(err, mode);
|
|
4699
4734
|
}
|
|
@@ -4854,10 +4889,17 @@ function formatAudit(payload) {
|
|
|
4854
4889
|
}
|
|
4855
4890
|
return lines.join("\n");
|
|
4856
4891
|
}
|
|
4857
|
-
function formatScaffold(payload, writeResult = null) {
|
|
4892
|
+
function formatScaffold(payload, writeResult = null, provisionResult = null) {
|
|
4858
4893
|
const lines = [
|
|
4859
4894
|
`Version: ${payload.version}`,
|
|
4860
4895
|
`Target: ${payload.target}`,
|
|
4896
|
+
`Owner: ${payload.ownerType}`,
|
|
4897
|
+
`Audience: ${payload.audience}`,
|
|
4898
|
+
`Tenancy: ${payload.tenancy}`,
|
|
4899
|
+
`Portfolio: ${payload.portfolio ?? "-"}`,
|
|
4900
|
+
`Surface: ${payload.surfaceModel}`,
|
|
4901
|
+
`Container: ${payload.containerAppId ?? "-"}`,
|
|
4902
|
+
`Distribution: ${payload.distributionModel}`,
|
|
4861
4903
|
`Application: ${payload.application.id}`,
|
|
4862
4904
|
`Directory: ${payload.application.appDir}`,
|
|
4863
4905
|
`Package: ${payload.application.packageName}`,
|
|
@@ -4886,13 +4928,54 @@ function formatScaffold(payload, writeResult = null) {
|
|
|
4886
4928
|
lines.push(` - ${file.status}: ${file.path}`);
|
|
4887
4929
|
}
|
|
4888
4930
|
}
|
|
4931
|
+
if (provisionResult) {
|
|
4932
|
+
lines.push("Provisioning:");
|
|
4933
|
+
lines.push(` - target: ${provisionResult.target}`);
|
|
4934
|
+
lines.push(
|
|
4935
|
+
` - actor: ${provisionResult.actor.email} (${provisionResult.actor.role}, org=${provisionResult.actor.organizationSlug ?? provisionResult.actor.organizationId ?? "-"})`
|
|
4936
|
+
);
|
|
4937
|
+
lines.push(` - developers origin: ${provisionResult.developersOrigin}`);
|
|
4938
|
+
lines.push(
|
|
4939
|
+
` - developers app: ${provisionResult.developersApp.id} (listing=${provisionResult.developersApp.listingStatus ?? "-"}, org=${provisionResult.developersApp.organizationId ?? "-"})`
|
|
4940
|
+
);
|
|
4941
|
+
if (provisionResult.authService) {
|
|
4942
|
+
lines.push(
|
|
4943
|
+
` - auth service: ${provisionResult.authService.id} (status=${provisionResult.authService.status ?? "-"}, url=${provisionResult.authService.baseUrl ?? "-"})`
|
|
4944
|
+
);
|
|
4945
|
+
}
|
|
4946
|
+
if (provisionResult.authConfig) {
|
|
4947
|
+
lines.push(
|
|
4948
|
+
` - auth config: login=${provisionResult.authConfig.login} landing=${provisionResult.authConfig.landing} app_ui=${provisionResult.authConfig.appUi}`
|
|
4949
|
+
);
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4889
4952
|
return lines.join("\n");
|
|
4890
4953
|
}
|
|
4891
|
-
function
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
if (normalized === "
|
|
4895
|
-
|
|
4954
|
+
function resolveScaffoldAudience(value) {
|
|
4955
|
+
if (!value) return void 0;
|
|
4956
|
+
const normalized = value.trim().toLowerCase();
|
|
4957
|
+
if (normalized === "business" || normalized === "personal") {
|
|
4958
|
+
return normalized;
|
|
4959
|
+
}
|
|
4960
|
+
throw new Error("Invalid --audience value. Use business or personal.");
|
|
4961
|
+
}
|
|
4962
|
+
function resolveScaffoldTenancy(value) {
|
|
4963
|
+
if (!value) return void 0;
|
|
4964
|
+
const normalized = value.trim().toLowerCase();
|
|
4965
|
+
if (normalized === "organization" || normalized === "household") {
|
|
4966
|
+
return normalized;
|
|
4967
|
+
}
|
|
4968
|
+
throw new Error("Invalid --tenancy value. Use organization or household.");
|
|
4969
|
+
}
|
|
4970
|
+
function resolveScaffoldSurfaceModel(value) {
|
|
4971
|
+
if (!value) return void 0;
|
|
4972
|
+
const normalized = value.trim().toLowerCase();
|
|
4973
|
+
if (normalized === "web_app" || normalized === "mobile_container" || normalized === "mobile_module" || normalized === "messaging_surface") {
|
|
4974
|
+
return normalized;
|
|
4975
|
+
}
|
|
4976
|
+
throw new Error(
|
|
4977
|
+
"Invalid --surface-model value. Use web_app, mobile_container, mobile_module, or messaging_surface."
|
|
4978
|
+
);
|
|
4896
4979
|
}
|
|
4897
4980
|
function resolveScaffoldApiOption(api, withApi) {
|
|
4898
4981
|
const noApi = process.argv.includes("--no-api");
|
|
@@ -4916,6 +4999,98 @@ function statusesAtOrWorse(threshold) {
|
|
|
4916
4999
|
return ["missing", "needs_review", "partial"];
|
|
4917
5000
|
}
|
|
4918
5001
|
|
|
5002
|
+
// src/commands/platform.ts
|
|
5003
|
+
function registerPlatformCommand(program2) {
|
|
5004
|
+
const platform3 = program2.command("platform").description("Internal Topolo first-party platform and personal app commands");
|
|
5005
|
+
const platformApps = platform3.command("apps").description("Scaffold and operate first-party Topolo platform and personal applications");
|
|
5006
|
+
platformApps.command("scaffold <application>").description(
|
|
5007
|
+
"Generate a first-party Topolo scaffold for the platform or personal portfolio."
|
|
5008
|
+
).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("--portfolio <portfolio>", "First-party portfolio: platform or personal").option(
|
|
5009
|
+
"--surface-model <surfaceModel>",
|
|
5010
|
+
"First-party surface model: web_app, mobile_container, mobile_module, or messaging_surface"
|
|
5011
|
+
).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(
|
|
5012
|
+
"--provision",
|
|
5013
|
+
"Provision the generated first-party app into Topolo Developers and Auth using the authenticated Topolo admin session"
|
|
5014
|
+
).option("--workflow-only", "Output only the generated GitHub Actions workflow in human mode").action(async (application, opts) => {
|
|
5015
|
+
const mode = opts.json === false || process.argv.includes("--no-json") ? "pretty" : resolveOutputMode(opts.json);
|
|
5016
|
+
try {
|
|
5017
|
+
await requirePlatformAdminSession();
|
|
5018
|
+
if (opts.workflowOnly && opts.write) {
|
|
5019
|
+
throw new Error("--workflow-only cannot be used with --write.");
|
|
5020
|
+
}
|
|
5021
|
+
const api = resolveScaffoldApiOption(opts.api, opts.withApi);
|
|
5022
|
+
const payload = applicationScaffoldPayload(application, {
|
|
5023
|
+
appDir: opts.appDir,
|
|
5024
|
+
name: opts.name,
|
|
5025
|
+
packageName: opts.packageName,
|
|
5026
|
+
productionUrl: opts.productionUrl,
|
|
5027
|
+
service: opts.service,
|
|
5028
|
+
api,
|
|
5029
|
+
browser: opts.browser,
|
|
5030
|
+
target: "platform",
|
|
5031
|
+
portfolio: resolvePlatformPortfolio(opts.portfolio),
|
|
5032
|
+
surfaceModel: resolvePlatformSurfaceModel(opts.surfaceModel),
|
|
5033
|
+
containerAppId: opts.containerAppId
|
|
5034
|
+
});
|
|
5035
|
+
if (opts.workflowOnly && !payload.ciWorkflow) {
|
|
5036
|
+
throw new Error("--workflow-only is only available for first-party scaffolds.");
|
|
5037
|
+
}
|
|
5038
|
+
if (opts.provision && !opts.write) {
|
|
5039
|
+
throw new Error("--provision requires --write so remote first-party records match written scaffold files.");
|
|
5040
|
+
}
|
|
5041
|
+
const writeResult = opts.write ? await writeApplicationScaffold(payload, { repoRoot: opts.repoRoot, force: opts.force }) : null;
|
|
5042
|
+
const provisionResult = opts.provision ? await provisionApplicationScaffold(payload) : null;
|
|
5043
|
+
const output = opts.workflowOnly ? { application: payload.application, ciWorkflowPath: payload.ciWorkflowPath, ciWorkflow: payload.ciWorkflow } : {
|
|
5044
|
+
...payload,
|
|
5045
|
+
...writeResult ? { write: writeResult } : {},
|
|
5046
|
+
...provisionResult ? { provision: provisionResult } : {}
|
|
5047
|
+
};
|
|
5048
|
+
emit(
|
|
5049
|
+
output,
|
|
5050
|
+
mode,
|
|
5051
|
+
() => opts.workflowOnly ? payload.ciWorkflow ?? "" : formatScaffold(payload, writeResult, provisionResult)
|
|
5052
|
+
);
|
|
5053
|
+
} catch (err) {
|
|
5054
|
+
fail(err, mode);
|
|
5055
|
+
}
|
|
5056
|
+
});
|
|
5057
|
+
}
|
|
5058
|
+
function resolvePlatformPortfolio(value) {
|
|
5059
|
+
if (!value) return void 0;
|
|
5060
|
+
const normalized = value.trim().toLowerCase();
|
|
5061
|
+
if (normalized === "platform" || normalized === "personal") {
|
|
5062
|
+
return normalized;
|
|
5063
|
+
}
|
|
5064
|
+
throw new Error("Invalid --portfolio value. Use platform or personal.");
|
|
5065
|
+
}
|
|
5066
|
+
function resolvePlatformSurfaceModel(value) {
|
|
5067
|
+
if (!value) return void 0;
|
|
5068
|
+
const normalized = value.trim().toLowerCase();
|
|
5069
|
+
if (normalized === "web_app" || normalized === "mobile_container" || normalized === "mobile_module" || normalized === "messaging_surface") {
|
|
5070
|
+
return normalized;
|
|
5071
|
+
}
|
|
5072
|
+
throw new Error(
|
|
5073
|
+
"Invalid --surface-model value. Use web_app, mobile_container, mobile_module, or messaging_surface."
|
|
5074
|
+
);
|
|
5075
|
+
}
|
|
5076
|
+
function isPlatformAdminIdentity(identity) {
|
|
5077
|
+
return String(identity?.user?.role || "").trim().toLowerCase() === "super_admin" && String(identity?.organization?.slug || "").trim().toLowerCase() === "admin";
|
|
5078
|
+
}
|
|
5079
|
+
async function requirePlatformAdminSession() {
|
|
5080
|
+
const client = await buildClient();
|
|
5081
|
+
if (!client) {
|
|
5082
|
+
throw new Error(
|
|
5083
|
+
"`topolo platform ...` requires an authenticated Topolo platform-admin session. Run `topolo auth login` first."
|
|
5084
|
+
);
|
|
5085
|
+
}
|
|
5086
|
+
const identity = await client.client.introspect();
|
|
5087
|
+
if (!isPlatformAdminIdentity(identity)) {
|
|
5088
|
+
throw new Error(
|
|
5089
|
+
"`topolo platform ...` is restricted to Topolo platform admins in the Auth `admin` organization."
|
|
5090
|
+
);
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
|
|
4919
5094
|
// src/commands/services.ts
|
|
4920
5095
|
function registerServicesCommand(program2) {
|
|
4921
5096
|
program2.command("services").description("List every Topolo service the SDK can route to").option("--json", "Output JSON", true).option("--no-json", "Output human-readable table").action((opts) => {
|
|
@@ -4954,13 +5129,14 @@ function formatRows2(rows) {
|
|
|
4954
5129
|
// src/index.ts
|
|
4955
5130
|
var program = new Command();
|
|
4956
5131
|
program.name("topolo").description(
|
|
4957
|
-
"Topolo
|
|
5132
|
+
"Topolo CLI. Public commands are scoped to the authenticated organization or developer workspace, and first-party Topolo scaffolding lives under the internal `topolo platform` namespace."
|
|
4958
5133
|
).version(CLI_VERSION);
|
|
4959
5134
|
registerAuthCommands(program);
|
|
4960
5135
|
registerWhoamiCommand(program);
|
|
4961
5136
|
registerCrmCommands(program);
|
|
4962
5137
|
registerApiCommand(program);
|
|
4963
5138
|
registerAppsCommand(program);
|
|
5139
|
+
registerPlatformCommand(program);
|
|
4964
5140
|
registerServicesCommand(program);
|
|
4965
5141
|
program.parseAsync(process.argv).catch((err) => {
|
|
4966
5142
|
process.stderr.write(`Unexpected error: ${err instanceof Error ? err.message : String(err)}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Command-line interface for
|
|
3
|
+
"version": "0.1.12",
|
|
4
|
+
"description": "Command-line interface for Topolo developers, operators, and agents, with internal first-party scaffolding available under the topolo platform namespace.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"topolo": "./dist/index.js"
|