@topolo/mcp 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -48
- package/dist/dispatch.d.ts +5 -0
- package/dist/dispatch.js +64 -143
- package/dist/http.d.ts +26 -3
- package/dist/http.js +326 -355
- package/dist/index.js +777 -2516
- package/package.json +5 -11
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
|
|
6
|
-
//
|
|
6
|
+
// ../../../public-platform-tooling-boundary-20260813/packages/topolo-sdk/dist/index.js
|
|
7
7
|
function applyAuthHeaders(headers, credential) {
|
|
8
8
|
if (credential.kind === "api_key") {
|
|
9
9
|
headers.set("X-Api-Key", credential.apiKey);
|
|
@@ -30,6 +30,90 @@ var TopoloSdkError = class extends Error {
|
|
|
30
30
|
this.code = code;
|
|
31
31
|
}
|
|
32
32
|
};
|
|
33
|
+
var PUBLIC_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
34
|
+
"action_app_identity_unavailable",
|
|
35
|
+
"action_authentication_required",
|
|
36
|
+
"action_catalog_summary_unavailable",
|
|
37
|
+
"action_catalog_unavailable",
|
|
38
|
+
"action_contract_mismatch",
|
|
39
|
+
"action_permission_required",
|
|
40
|
+
"action_runtime_authorization_required",
|
|
41
|
+
"action_service_not_allowed",
|
|
42
|
+
"auth_error",
|
|
43
|
+
"authentication_required",
|
|
44
|
+
"authorization_pending",
|
|
45
|
+
"bad_sig",
|
|
46
|
+
"confirmation_required",
|
|
47
|
+
"dependency_unavailable",
|
|
48
|
+
"environment_mismatch",
|
|
49
|
+
"environment_service_url_required",
|
|
50
|
+
"expired_token",
|
|
51
|
+
"forbidden",
|
|
52
|
+
"http_error",
|
|
53
|
+
"immutable_drift",
|
|
54
|
+
"integration_dependency_unavailable",
|
|
55
|
+
"integration_misconfigured",
|
|
56
|
+
"invalid_action",
|
|
57
|
+
"invalid_action_input",
|
|
58
|
+
"invalid_grant",
|
|
59
|
+
"invalid_input",
|
|
60
|
+
"invalid_token",
|
|
61
|
+
"mcp_error",
|
|
62
|
+
"membership_required",
|
|
63
|
+
"network_error",
|
|
64
|
+
"organization_context_required",
|
|
65
|
+
"organization_enrollment_required",
|
|
66
|
+
"permission_denied",
|
|
67
|
+
"rate_limited",
|
|
68
|
+
"request_failed",
|
|
69
|
+
"service_catalog_unavailable",
|
|
70
|
+
"service_path_origin_mismatch",
|
|
71
|
+
"service_required",
|
|
72
|
+
"slow_down",
|
|
73
|
+
"state_conflict",
|
|
74
|
+
"unauthorized",
|
|
75
|
+
"unknown_action",
|
|
76
|
+
"unknown_service"
|
|
77
|
+
]);
|
|
78
|
+
var ERROR_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
|
|
79
|
+
var TOPOLO_REQUEST_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
80
|
+
var CLOUDFLARE_RAY_ID_PATTERN = /^[0-9a-f]{16}-[A-Z]{3}$/;
|
|
81
|
+
var PERMISSION_PATTERN = /^[a-z][a-z0-9_-]{0,63}(?:\.[a-z][a-z0-9_-]{0,63})*:(?:[a-z][a-z0-9_-]{0,63}|\*)$/;
|
|
82
|
+
var RETRY_AFTER_DATE_PATTERN = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
|
|
83
|
+
var PUBLIC_DETAIL_SOURCES = /* @__PURE__ */ new Set([
|
|
84
|
+
"api_key_introspection",
|
|
85
|
+
"api_key_validation",
|
|
86
|
+
"auth_context",
|
|
87
|
+
"auth_validate",
|
|
88
|
+
"catalog",
|
|
89
|
+
"credential",
|
|
90
|
+
"runtime",
|
|
91
|
+
"service_token_provider",
|
|
92
|
+
"service_verify_key",
|
|
93
|
+
"token_validation",
|
|
94
|
+
"upstream"
|
|
95
|
+
]);
|
|
96
|
+
function canonicalTopoloErrorCode(value) {
|
|
97
|
+
if (typeof value !== "string") return null;
|
|
98
|
+
const candidate = value.trim().toLowerCase();
|
|
99
|
+
if (!ERROR_CODE_PATTERN.test(candidate) || !PUBLIC_ERROR_CODES.has(candidate)) return null;
|
|
100
|
+
return candidate;
|
|
101
|
+
}
|
|
102
|
+
function canonicalTopoloRequestId(value) {
|
|
103
|
+
if (typeof value !== "string") return void 0;
|
|
104
|
+
const candidate = value.trim();
|
|
105
|
+
return TOPOLO_REQUEST_ID_PATTERN.test(candidate) || CLOUDFLARE_RAY_ID_PATTERN.test(candidate) ? candidate : void 0;
|
|
106
|
+
}
|
|
107
|
+
function canonicalTopoloRetryAfter(value) {
|
|
108
|
+
if (typeof value !== "string") return void 0;
|
|
109
|
+
const candidate = value.trim();
|
|
110
|
+
if (/^\d{1,10}$/.test(candidate) || RETRY_AFTER_DATE_PATTERN.test(candidate)) return candidate;
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
113
|
+
function canonicalTopoloRequired(value) {
|
|
114
|
+
if (!Array.isArray(value)) return [];
|
|
115
|
+
return [...new Set(value.filter((item) => typeof item === "string" && PERMISSION_PATTERN.test(item)))].slice(0, 32);
|
|
116
|
+
}
|
|
33
117
|
var TopoloAuthError = class extends TopoloSdkError {
|
|
34
118
|
status;
|
|
35
119
|
body;
|
|
@@ -37,15 +121,19 @@ var TopoloAuthError = class extends TopoloSdkError {
|
|
|
37
121
|
path;
|
|
38
122
|
requestId;
|
|
39
123
|
retryAfter;
|
|
124
|
+
details;
|
|
40
125
|
constructor(message, code = "auth_error", options = {}) {
|
|
41
|
-
super(code, message);
|
|
126
|
+
super(canonicalTopoloErrorCode(code) ?? "auth_error", message);
|
|
42
127
|
this.name = "TopoloAuthError";
|
|
43
128
|
this.status = options.status ?? 401;
|
|
44
129
|
this.body = options.body;
|
|
45
130
|
this.service = options.service;
|
|
46
131
|
this.path = options.path;
|
|
47
|
-
this.requestId = options.requestId;
|
|
48
|
-
this.retryAfter = options.retryAfter;
|
|
132
|
+
this.requestId = canonicalTopoloRequestId(options.requestId);
|
|
133
|
+
this.retryAfter = canonicalTopoloRetryAfter(options.retryAfter);
|
|
134
|
+
this.details = extractTopoloServiceErrorDetails(
|
|
135
|
+
options.details === void 0 ? options.body : { details: options.details }
|
|
136
|
+
);
|
|
49
137
|
}
|
|
50
138
|
};
|
|
51
139
|
var TopoloPermissionError = class extends TopoloSdkError {
|
|
@@ -56,28 +144,140 @@ var TopoloPermissionError = class extends TopoloSdkError {
|
|
|
56
144
|
path;
|
|
57
145
|
requestId;
|
|
58
146
|
retryAfter;
|
|
147
|
+
details;
|
|
59
148
|
constructor(message, required, options = {}) {
|
|
60
|
-
super(
|
|
149
|
+
super(
|
|
150
|
+
canonicalTopoloErrorCode(options.code) ?? serviceErrorCode(options.body, "permission_denied"),
|
|
151
|
+
message
|
|
152
|
+
);
|
|
61
153
|
this.name = "TopoloPermissionError";
|
|
62
|
-
this.required = required;
|
|
154
|
+
this.required = canonicalTopoloRequired(required);
|
|
63
155
|
this.status = options.status ?? 403;
|
|
64
156
|
this.body = options.body;
|
|
65
157
|
this.service = options.service;
|
|
66
158
|
this.path = options.path;
|
|
67
|
-
this.requestId = options.requestId;
|
|
68
|
-
this.retryAfter = options.retryAfter;
|
|
159
|
+
this.requestId = canonicalTopoloRequestId(options.requestId);
|
|
160
|
+
this.retryAfter = canonicalTopoloRetryAfter(options.retryAfter);
|
|
161
|
+
this.details = extractTopoloServiceErrorDetails(
|
|
162
|
+
options.details === void 0 ? options.body : { details: options.details }
|
|
163
|
+
);
|
|
69
164
|
}
|
|
70
165
|
};
|
|
71
|
-
function
|
|
72
|
-
if (!body || typeof body !== "object" || Array.isArray(body)) return
|
|
166
|
+
function extractTopoloServiceErrorCode(body) {
|
|
167
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
73
168
|
const record = body;
|
|
74
|
-
|
|
169
|
+
const topLevel = canonicalTopoloErrorCode(record.code);
|
|
170
|
+
if (topLevel) return topLevel;
|
|
75
171
|
const nested = record.error;
|
|
172
|
+
const nestedString = canonicalTopoloErrorCode(nested);
|
|
173
|
+
if (nestedString) return nestedString;
|
|
76
174
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
77
175
|
const code = nested.code;
|
|
78
|
-
|
|
176
|
+
const nestedCode = canonicalTopoloErrorCode(code);
|
|
177
|
+
if (nestedCode) return nestedCode;
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
function serviceErrorCode(body, fallback) {
|
|
182
|
+
return extractTopoloServiceErrorCode(body) ?? fallback;
|
|
183
|
+
}
|
|
184
|
+
function extractTopoloServiceErrorDetails(body) {
|
|
185
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return void 0;
|
|
186
|
+
const record = body;
|
|
187
|
+
let candidate = record.details;
|
|
188
|
+
const nested = record.error;
|
|
189
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
190
|
+
const nestedRecord = nested;
|
|
191
|
+
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
192
|
+
}
|
|
193
|
+
if (candidate === void 0) candidate = record;
|
|
194
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
|
|
195
|
+
const source = candidate;
|
|
196
|
+
const safe = {};
|
|
197
|
+
for (const [key, value] of Object.entries(source)) {
|
|
198
|
+
if (/present$/i.test(key) && typeof value === "boolean") {
|
|
199
|
+
safe[key] = value;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (key === "retryable" && typeof value === "boolean") {
|
|
203
|
+
safe.retryable = value;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (key === "code") {
|
|
207
|
+
const sanitized = canonicalTopoloErrorCode(value);
|
|
208
|
+
if (sanitized) safe.code = sanitized;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (key === "source") {
|
|
212
|
+
const sanitized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
213
|
+
if (PUBLIC_DETAIL_SOURCES.has(sanitized)) safe.source = sanitized;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (key === "dependency" || key === "platform") {
|
|
217
|
+
safe[`${key}Present`] = value !== null && value !== void 0;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (key === "error" && !safe.code) {
|
|
221
|
+
const sanitized = canonicalTopoloErrorCode(value);
|
|
222
|
+
if (sanitized) safe.code = sanitized;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (key === "required" && Array.isArray(value)) {
|
|
226
|
+
const required = canonicalTopoloRequired(value);
|
|
227
|
+
if (required.length) safe.required = required;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (["missingFields", "fields", "paths"].includes(key) && Array.isArray(value)) {
|
|
231
|
+
const paths = value.filter((item) => typeof item === "string" && /^[$.a-z0-9_[\]-]{1,160}$/i.test(item));
|
|
232
|
+
if (paths.length) safe[key] = paths;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return Object.keys(safe).length ? safe : void 0;
|
|
236
|
+
}
|
|
237
|
+
function publicTopoloErrorMessage(error) {
|
|
238
|
+
const code = canonicalTopoloErrorCode(
|
|
239
|
+
typeof error === "string" ? error : error.code ?? extractTopoloServiceErrorCode(error.body)
|
|
240
|
+
) ?? "request_failed";
|
|
241
|
+
if (/(?:organization|org).*(?:enroll|membership|member|admission)/.test(code)) {
|
|
242
|
+
return "Organization or application enrollment is required.";
|
|
243
|
+
}
|
|
244
|
+
if (/(?:organization|org).*(?:context|required|missing|invalid|unknown|not_found)/.test(code)) {
|
|
245
|
+
return "Organization context is unavailable.";
|
|
246
|
+
}
|
|
247
|
+
if (["auth_error", "authentication_required", "unauthorized", "invalid_token", "expired_token"].includes(code)) {
|
|
248
|
+
return "Authentication failed.";
|
|
249
|
+
}
|
|
250
|
+
if (code === "permission_denied" || code === "forbidden") return "Permission denied.";
|
|
251
|
+
if (code === "confirmation_required") return "Confirmation is required.";
|
|
252
|
+
return `Topolo request failed (${code}).`;
|
|
253
|
+
}
|
|
254
|
+
function topoloErrorHint(error) {
|
|
255
|
+
const code = canonicalTopoloErrorCode(
|
|
256
|
+
typeof error === "string" ? error : error.code ?? extractTopoloServiceErrorCode(error.body)
|
|
257
|
+
);
|
|
258
|
+
if (!code) return void 0;
|
|
259
|
+
if (code === "auth_error" || code === "authentication_required" || code === "unauthorized" || code === "invalid_token" || code === "expired_token") {
|
|
260
|
+
return "Run `topolo auth login`, then verify the effective Topolo identity and organization.";
|
|
261
|
+
}
|
|
262
|
+
if (code === "confirmation_required") {
|
|
263
|
+
return "Review the unchanged action plan, obtain approval, and retry once with --confirm or confirm:true.";
|
|
264
|
+
}
|
|
265
|
+
if (code === "enrollment_required" || code === "membership_required" || /(?:organization|org).*(?:enroll|membership|member|admission)|(?:enroll|membership).*(?:organization|org)/.test(code)) {
|
|
266
|
+
return "The current identity is not enrolled for this organization or application. Use an enrolled identity or ask an organization administrator to complete enrollment.";
|
|
267
|
+
}
|
|
268
|
+
if (/(?:organization|org).*(?:context|required|missing|invalid|unknown|not_found)/.test(code)) {
|
|
269
|
+
return "Select a signed-in organization context, then verify the effective identity and organization before retrying.";
|
|
79
270
|
}
|
|
80
|
-
|
|
271
|
+
if (code === "permission_denied" || code === "forbidden") {
|
|
272
|
+
return "The current identity or resource context is not authorized. Review the structured details and published authorization contract.";
|
|
273
|
+
}
|
|
274
|
+
if (code === "environment_mismatch") {
|
|
275
|
+
return "Use service origins and credentials from one explicit Topolo environment only.";
|
|
276
|
+
}
|
|
277
|
+
if (code === "invalid_action_input" || code === "invalid_input") {
|
|
278
|
+
return "Correct only the reported schema paths, then prepare the action again.";
|
|
279
|
+
}
|
|
280
|
+
return void 0;
|
|
81
281
|
}
|
|
82
282
|
var TopoloConfirmationRequiredError = class extends TopoloSdkError {
|
|
83
283
|
constructor(message) {
|
|
@@ -92,2282 +292,78 @@ var TopoloHttpError = class extends TopoloSdkError {
|
|
|
92
292
|
path;
|
|
93
293
|
requestId;
|
|
94
294
|
retryAfter;
|
|
295
|
+
details;
|
|
95
296
|
constructor(service, path, status, body, message, options = {}) {
|
|
96
|
-
super(
|
|
297
|
+
super(serviceErrorCode(body, "http_error"), message ?? `HTTP ${status} from ${service}${path}`);
|
|
97
298
|
this.name = "TopoloHttpError";
|
|
98
299
|
this.status = status;
|
|
99
300
|
this.body = body;
|
|
100
301
|
this.service = service;
|
|
101
|
-
this.path = path;
|
|
102
|
-
this.requestId = options.requestId;
|
|
103
|
-
this.retryAfter = options.retryAfter;
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
var APP_API_URLS = {
|
|
107
|
-
auth: "https://auth.topolo.app",
|
|
108
|
-
developers: "https://developers.topolo.app"
|
|
109
|
-
};
|
|
110
|
-
var APPLICATIONS = {
|
|
111
|
-
"admin": {
|
|
112
|
-
"id": "admin",
|
|
113
|
-
"appDir": "TopoloAdmin",
|
|
114
|
-
"name": "topolo-admin",
|
|
115
|
-
"packageName": "topolo-admin",
|
|
116
|
-
"productionUrl": "https://admin.topolo.app",
|
|
117
|
-
"services": [],
|
|
118
|
-
"deployTargets": [
|
|
119
|
-
{
|
|
120
|
-
"name": "topolo-admin",
|
|
121
|
-
"kind": "worker",
|
|
122
|
-
"apiUrl": "https://admin.topolo.app",
|
|
123
|
-
"domains": [
|
|
124
|
-
"admin.topolo.app"
|
|
125
|
-
],
|
|
126
|
-
"routes": [
|
|
127
|
-
"admin.topolo.app"
|
|
128
|
-
],
|
|
129
|
-
"appId": null
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
"name": "topolo-admin-api",
|
|
133
|
-
"kind": "worker",
|
|
134
|
-
"apiUrl": null,
|
|
135
|
-
"domains": [],
|
|
136
|
-
"routes": [],
|
|
137
|
-
"appId": null
|
|
138
|
-
}
|
|
139
|
-
]
|
|
140
|
-
},
|
|
141
|
-
"agent": {
|
|
142
|
-
"id": "agent",
|
|
143
|
-
"appDir": "TopoloAgent",
|
|
144
|
-
"name": "topolo-agent",
|
|
145
|
-
"packageName": "topolo-agent",
|
|
146
|
-
"productionUrl": "https://agent.topolo.app",
|
|
147
|
-
"services": [
|
|
148
|
-
"agent"
|
|
149
|
-
],
|
|
150
|
-
"deployTargets": [
|
|
151
|
-
{
|
|
152
|
-
"name": "topolo-agent",
|
|
153
|
-
"kind": "worker",
|
|
154
|
-
"apiUrl": "https://agent.topolo.app",
|
|
155
|
-
"domains": [
|
|
156
|
-
"agent.topolo.app"
|
|
157
|
-
],
|
|
158
|
-
"routes": [
|
|
159
|
-
"agent.topolo.app"
|
|
160
|
-
],
|
|
161
|
-
"appId": "agent"
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
"name": "topolo-agent-api",
|
|
165
|
-
"kind": "worker",
|
|
166
|
-
"apiUrl": null,
|
|
167
|
-
"domains": [],
|
|
168
|
-
"routes": [],
|
|
169
|
-
"appId": null
|
|
170
|
-
}
|
|
171
|
-
]
|
|
172
|
-
},
|
|
173
|
-
"auth": {
|
|
174
|
-
"id": "auth",
|
|
175
|
-
"appDir": "TopoloAuth",
|
|
176
|
-
"name": "topolo-auth",
|
|
177
|
-
"packageName": "topolo-auth",
|
|
178
|
-
"productionUrl": "https://auth.topolo.app",
|
|
179
|
-
"services": [
|
|
180
|
-
"auth"
|
|
181
|
-
],
|
|
182
|
-
"deployTargets": [
|
|
183
|
-
{
|
|
184
|
-
"name": "topolo-auth",
|
|
185
|
-
"kind": "worker",
|
|
186
|
-
"apiUrl": "https://auth.topolo.app",
|
|
187
|
-
"domains": [
|
|
188
|
-
"auth.topolo.app"
|
|
189
|
-
],
|
|
190
|
-
"routes": [
|
|
191
|
-
"auth.topolo.app"
|
|
192
|
-
],
|
|
193
|
-
"appId": "auth"
|
|
194
|
-
}
|
|
195
|
-
]
|
|
196
|
-
},
|
|
197
|
-
"backup": {
|
|
198
|
-
"id": "backup",
|
|
199
|
-
"appDir": "TopoloBackup",
|
|
200
|
-
"name": "topolo-backup",
|
|
201
|
-
"packageName": "topolo-backup",
|
|
202
|
-
"productionUrl": "https://backup.topolo.app",
|
|
203
|
-
"services": [
|
|
204
|
-
"backup"
|
|
205
|
-
],
|
|
206
|
-
"deployTargets": [
|
|
207
|
-
{
|
|
208
|
-
"name": "topolo-backup",
|
|
209
|
-
"kind": "worker",
|
|
210
|
-
"apiUrl": "https://backup.topolo.app",
|
|
211
|
-
"domains": [
|
|
212
|
-
"backup.topolo.app"
|
|
213
|
-
],
|
|
214
|
-
"routes": [
|
|
215
|
-
"backup.topolo.app"
|
|
216
|
-
],
|
|
217
|
-
"appId": "backup"
|
|
218
|
-
}
|
|
219
|
-
]
|
|
220
|
-
},
|
|
221
|
-
"blog": {
|
|
222
|
-
"id": "blog",
|
|
223
|
-
"appDir": "TopoloBlog",
|
|
224
|
-
"name": "topolo-blog",
|
|
225
|
-
"packageName": "topolo-blog",
|
|
226
|
-
"productionUrl": "https://blog.topolo.app",
|
|
227
|
-
"services": [
|
|
228
|
-
"blog"
|
|
229
|
-
],
|
|
230
|
-
"deployTargets": [
|
|
231
|
-
{
|
|
232
|
-
"name": "topolo-blog",
|
|
233
|
-
"kind": "worker",
|
|
234
|
-
"apiUrl": "https://blog.topolo.app",
|
|
235
|
-
"domains": [
|
|
236
|
-
"blog.topolo.app"
|
|
237
|
-
],
|
|
238
|
-
"routes": [
|
|
239
|
-
"blog.topolo.app"
|
|
240
|
-
],
|
|
241
|
-
"appId": "blog"
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
"name": "topolo-blog-api",
|
|
245
|
-
"kind": "worker",
|
|
246
|
-
"apiUrl": null,
|
|
247
|
-
"domains": [],
|
|
248
|
-
"routes": [],
|
|
249
|
-
"appId": null
|
|
250
|
-
}
|
|
251
|
-
]
|
|
252
|
-
},
|
|
253
|
-
"books": {
|
|
254
|
-
"id": "books",
|
|
255
|
-
"appDir": "TopoloBooks",
|
|
256
|
-
"name": "topolo-books",
|
|
257
|
-
"packageName": "topolo-books",
|
|
258
|
-
"productionUrl": "https://books.topolo.app",
|
|
259
|
-
"services": [
|
|
260
|
-
"books"
|
|
261
|
-
],
|
|
262
|
-
"deployTargets": [
|
|
263
|
-
{
|
|
264
|
-
"name": "topolo-books",
|
|
265
|
-
"kind": "worker",
|
|
266
|
-
"apiUrl": "https://books.topolo.app",
|
|
267
|
-
"domains": [
|
|
268
|
-
"books.topolo.app"
|
|
269
|
-
],
|
|
270
|
-
"routes": [
|
|
271
|
-
"books.topolo.app"
|
|
272
|
-
],
|
|
273
|
-
"appId": "books"
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
"name": "topolo-books-api",
|
|
277
|
-
"kind": "worker",
|
|
278
|
-
"apiUrl": null,
|
|
279
|
-
"domains": [],
|
|
280
|
-
"routes": [],
|
|
281
|
-
"appId": null
|
|
282
|
-
}
|
|
283
|
-
]
|
|
284
|
-
},
|
|
285
|
-
"brand": {
|
|
286
|
-
"id": "brand",
|
|
287
|
-
"appDir": "TopoloBrand",
|
|
288
|
-
"name": "topolo-brand",
|
|
289
|
-
"packageName": "@topolo/topolo-brand",
|
|
290
|
-
"productionUrl": "https://brand.topolo.app",
|
|
291
|
-
"services": [
|
|
292
|
-
"brand"
|
|
293
|
-
],
|
|
294
|
-
"deployTargets": [
|
|
295
|
-
{
|
|
296
|
-
"name": "topolo-brand",
|
|
297
|
-
"kind": "worker",
|
|
298
|
-
"apiUrl": "https://brand.topolo.app",
|
|
299
|
-
"domains": [
|
|
300
|
-
"brand.topolo.app"
|
|
301
|
-
],
|
|
302
|
-
"routes": [
|
|
303
|
-
"brand.topolo.app"
|
|
304
|
-
],
|
|
305
|
-
"appId": "brand"
|
|
306
|
-
},
|
|
307
|
-
{
|
|
308
|
-
"name": "topolo-brand-api",
|
|
309
|
-
"kind": "worker",
|
|
310
|
-
"apiUrl": null,
|
|
311
|
-
"domains": [],
|
|
312
|
-
"routes": [],
|
|
313
|
-
"appId": null
|
|
314
|
-
}
|
|
315
|
-
]
|
|
316
|
-
},
|
|
317
|
-
"bugfix": {
|
|
318
|
-
"id": "bugfix",
|
|
319
|
-
"appDir": "TopoloBugFix",
|
|
320
|
-
"name": "topolo-bugfix",
|
|
321
|
-
"packageName": "topolo-bugfix",
|
|
322
|
-
"productionUrl": "https://bugfix.topolo.app",
|
|
323
|
-
"services": [
|
|
324
|
-
"bugfix"
|
|
325
|
-
],
|
|
326
|
-
"deployTargets": [
|
|
327
|
-
{
|
|
328
|
-
"name": "topolo-bugfix",
|
|
329
|
-
"kind": "worker",
|
|
330
|
-
"apiUrl": "https://bugfix.topolo.app",
|
|
331
|
-
"domains": [
|
|
332
|
-
"bugfix.topolo.app"
|
|
333
|
-
],
|
|
334
|
-
"routes": [
|
|
335
|
-
"bugfix.topolo.app"
|
|
336
|
-
],
|
|
337
|
-
"appId": "bugfix"
|
|
338
|
-
}
|
|
339
|
-
]
|
|
340
|
-
},
|
|
341
|
-
"bytes": {
|
|
342
|
-
"id": "bytes",
|
|
343
|
-
"appDir": "TopoloBytes",
|
|
344
|
-
"name": "topolo-bytes",
|
|
345
|
-
"packageName": "topolo-bytes",
|
|
346
|
-
"productionUrl": "https://bytes.topolo.app",
|
|
347
|
-
"services": [
|
|
348
|
-
"bytes"
|
|
349
|
-
],
|
|
350
|
-
"deployTargets": [
|
|
351
|
-
{
|
|
352
|
-
"name": "topolo-bytes",
|
|
353
|
-
"kind": "worker",
|
|
354
|
-
"apiUrl": "https://bytes.topolo.app",
|
|
355
|
-
"domains": [
|
|
356
|
-
"bytes.topolo.app"
|
|
357
|
-
],
|
|
358
|
-
"routes": [
|
|
359
|
-
"bytes.topolo.app"
|
|
360
|
-
],
|
|
361
|
-
"appId": "bytes"
|
|
362
|
-
},
|
|
363
|
-
{
|
|
364
|
-
"name": "topolo-bytes-api",
|
|
365
|
-
"kind": "worker",
|
|
366
|
-
"apiUrl": null,
|
|
367
|
-
"domains": [],
|
|
368
|
-
"routes": [],
|
|
369
|
-
"appId": null
|
|
370
|
-
}
|
|
371
|
-
]
|
|
372
|
-
},
|
|
373
|
-
"calendar": {
|
|
374
|
-
"id": "calendar",
|
|
375
|
-
"appDir": "TopoloCalendar",
|
|
376
|
-
"name": "topolo-calendar",
|
|
377
|
-
"packageName": "topolo-calendar",
|
|
378
|
-
"productionUrl": "https://calendar.topolo.app",
|
|
379
|
-
"services": [
|
|
380
|
-
"calendar"
|
|
381
|
-
],
|
|
382
|
-
"deployTargets": [
|
|
383
|
-
{
|
|
384
|
-
"name": "topolo-calendar",
|
|
385
|
-
"kind": "worker",
|
|
386
|
-
"apiUrl": "https://calendar.topolo.app",
|
|
387
|
-
"domains": [
|
|
388
|
-
"calendar.topolo.app"
|
|
389
|
-
],
|
|
390
|
-
"routes": [
|
|
391
|
-
"calendar.topolo.app"
|
|
392
|
-
],
|
|
393
|
-
"appId": "calendar"
|
|
394
|
-
},
|
|
395
|
-
{
|
|
396
|
-
"name": "topolo-calendar-api",
|
|
397
|
-
"kind": "worker",
|
|
398
|
-
"apiUrl": null,
|
|
399
|
-
"domains": [],
|
|
400
|
-
"routes": [],
|
|
401
|
-
"appId": null
|
|
402
|
-
}
|
|
403
|
-
]
|
|
404
|
-
},
|
|
405
|
-
"campaigns": {
|
|
406
|
-
"id": "campaigns",
|
|
407
|
-
"appDir": "TopoloCampaigns",
|
|
408
|
-
"name": "topolo-campaigns",
|
|
409
|
-
"packageName": "topolo-campaigns",
|
|
410
|
-
"productionUrl": "https://campaigns.topolo.app",
|
|
411
|
-
"services": [
|
|
412
|
-
"campaigns"
|
|
413
|
-
],
|
|
414
|
-
"deployTargets": [
|
|
415
|
-
{
|
|
416
|
-
"name": "topolo-campaigns",
|
|
417
|
-
"kind": "worker",
|
|
418
|
-
"apiUrl": "https://campaigns.topolo.app",
|
|
419
|
-
"domains": [
|
|
420
|
-
"campaigns.topolo.app"
|
|
421
|
-
],
|
|
422
|
-
"routes": [
|
|
423
|
-
"campaigns.topolo.app"
|
|
424
|
-
],
|
|
425
|
-
"appId": "campaigns"
|
|
426
|
-
},
|
|
427
|
-
{
|
|
428
|
-
"name": "topolo-campaigns-api",
|
|
429
|
-
"kind": "worker",
|
|
430
|
-
"apiUrl": null,
|
|
431
|
-
"domains": [],
|
|
432
|
-
"routes": [],
|
|
433
|
-
"appId": null
|
|
434
|
-
}
|
|
435
|
-
]
|
|
436
|
-
},
|
|
437
|
-
"capacity": {
|
|
438
|
-
"id": "capacity",
|
|
439
|
-
"appDir": "TopoloCapacity",
|
|
440
|
-
"name": "topolo-capacity",
|
|
441
|
-
"packageName": "topolo-capacity",
|
|
442
|
-
"productionUrl": "https://capacity.topolo.app",
|
|
443
|
-
"services": [
|
|
444
|
-
"capacity"
|
|
445
|
-
],
|
|
446
|
-
"deployTargets": [
|
|
447
|
-
{
|
|
448
|
-
"name": "topolo-capacity",
|
|
449
|
-
"kind": "worker",
|
|
450
|
-
"apiUrl": "https://capacity.topolo.app",
|
|
451
|
-
"domains": [
|
|
452
|
-
"capacity.topolo.app"
|
|
453
|
-
],
|
|
454
|
-
"routes": [
|
|
455
|
-
"capacity.topolo.app"
|
|
456
|
-
],
|
|
457
|
-
"appId": "capacity"
|
|
458
|
-
}
|
|
459
|
-
]
|
|
460
|
-
},
|
|
461
|
-
"chat": {
|
|
462
|
-
"id": "chat",
|
|
463
|
-
"appDir": "TopoloChat",
|
|
464
|
-
"name": "topolo-chat",
|
|
465
|
-
"packageName": "topolo-chat",
|
|
466
|
-
"productionUrl": "https://chat.topolo.app",
|
|
467
|
-
"services": [
|
|
468
|
-
"chat"
|
|
469
|
-
],
|
|
470
|
-
"deployTargets": [
|
|
471
|
-
{
|
|
472
|
-
"name": "topolo-chat",
|
|
473
|
-
"kind": "worker",
|
|
474
|
-
"apiUrl": "https://chat.topolo.app",
|
|
475
|
-
"domains": [
|
|
476
|
-
"chat.topolo.app"
|
|
477
|
-
],
|
|
478
|
-
"routes": [
|
|
479
|
-
"chat.topolo.app"
|
|
480
|
-
],
|
|
481
|
-
"appId": "chat"
|
|
482
|
-
},
|
|
483
|
-
{
|
|
484
|
-
"name": "topolo-chat-api",
|
|
485
|
-
"kind": "worker",
|
|
486
|
-
"apiUrl": null,
|
|
487
|
-
"domains": [],
|
|
488
|
-
"routes": [],
|
|
489
|
-
"appId": null
|
|
490
|
-
}
|
|
491
|
-
]
|
|
492
|
-
},
|
|
493
|
-
"commerce": {
|
|
494
|
-
"id": "commerce",
|
|
495
|
-
"appDir": "TopoloCommerce",
|
|
496
|
-
"name": "topolo-commerce",
|
|
497
|
-
"packageName": "topolo-commerce",
|
|
498
|
-
"productionUrl": "https://commerce.topolo.app",
|
|
499
|
-
"services": [
|
|
500
|
-
"commerce"
|
|
501
|
-
],
|
|
502
|
-
"deployTargets": [
|
|
503
|
-
{
|
|
504
|
-
"name": "topolo-commerce-api",
|
|
505
|
-
"kind": "worker",
|
|
506
|
-
"apiUrl": null,
|
|
507
|
-
"domains": [],
|
|
508
|
-
"routes": [],
|
|
509
|
-
"appId": null
|
|
510
|
-
},
|
|
511
|
-
{
|
|
512
|
-
"name": "topolo-commerce-guest-web",
|
|
513
|
-
"kind": "worker",
|
|
514
|
-
"apiUrl": "https://guest.topolo.app",
|
|
515
|
-
"domains": [
|
|
516
|
-
"guest.topolo.app"
|
|
517
|
-
],
|
|
518
|
-
"routes": [
|
|
519
|
-
"guest.topolo.app"
|
|
520
|
-
],
|
|
521
|
-
"appId": null
|
|
522
|
-
},
|
|
523
|
-
{
|
|
524
|
-
"name": "topolo-commerce-ops-web",
|
|
525
|
-
"kind": "worker",
|
|
526
|
-
"apiUrl": "https://commerce.topolo.app",
|
|
527
|
-
"domains": [
|
|
528
|
-
"commerce.topolo.app"
|
|
529
|
-
],
|
|
530
|
-
"routes": [
|
|
531
|
-
"commerce.topolo.app"
|
|
532
|
-
],
|
|
533
|
-
"appId": "commerce"
|
|
534
|
-
},
|
|
535
|
-
{
|
|
536
|
-
"name": "topolo-commerce-station-web",
|
|
537
|
-
"kind": "worker",
|
|
538
|
-
"apiUrl": "https://station-commerce.topolo.app",
|
|
539
|
-
"domains": [
|
|
540
|
-
"station-commerce.topolo.app"
|
|
541
|
-
],
|
|
542
|
-
"routes": [
|
|
543
|
-
"station-commerce.topolo.app"
|
|
544
|
-
],
|
|
545
|
-
"appId": null
|
|
546
|
-
}
|
|
547
|
-
]
|
|
548
|
-
},
|
|
549
|
-
"compose": {
|
|
550
|
-
"id": "compose",
|
|
551
|
-
"appDir": "TopoloCompose",
|
|
552
|
-
"name": "topolo-compose",
|
|
553
|
-
"packageName": "@topolo/compose",
|
|
554
|
-
"productionUrl": "https://compose.topolo.app",
|
|
555
|
-
"services": [
|
|
556
|
-
"compose"
|
|
557
|
-
],
|
|
558
|
-
"deployTargets": [
|
|
559
|
-
{
|
|
560
|
-
"name": "topolo-compose",
|
|
561
|
-
"kind": "worker",
|
|
562
|
-
"apiUrl": "https://compose.topolo.app",
|
|
563
|
-
"domains": [
|
|
564
|
-
"compose.topolo.app"
|
|
565
|
-
],
|
|
566
|
-
"routes": [
|
|
567
|
-
"compose.topolo.app"
|
|
568
|
-
],
|
|
569
|
-
"appId": "compose"
|
|
570
|
-
},
|
|
571
|
-
{
|
|
572
|
-
"name": "topolo-compose-api",
|
|
573
|
-
"kind": "worker",
|
|
574
|
-
"apiUrl": null,
|
|
575
|
-
"domains": [],
|
|
576
|
-
"routes": [],
|
|
577
|
-
"appId": null
|
|
578
|
-
}
|
|
579
|
-
]
|
|
580
|
-
},
|
|
581
|
-
"consent": {
|
|
582
|
-
"id": "consent",
|
|
583
|
-
"appDir": "TopoloConsent",
|
|
584
|
-
"name": "topolo-consent",
|
|
585
|
-
"packageName": "topolo-consent",
|
|
586
|
-
"productionUrl": "https://consent.topolo.app",
|
|
587
|
-
"services": [
|
|
588
|
-
"consent"
|
|
589
|
-
],
|
|
590
|
-
"deployTargets": [
|
|
591
|
-
{
|
|
592
|
-
"name": "topolo-consent",
|
|
593
|
-
"kind": "worker",
|
|
594
|
-
"apiUrl": "https://consent.topolo.app",
|
|
595
|
-
"domains": [
|
|
596
|
-
"consent.topolo.app"
|
|
597
|
-
],
|
|
598
|
-
"routes": [
|
|
599
|
-
"consent.topolo.app"
|
|
600
|
-
],
|
|
601
|
-
"appId": "consent"
|
|
602
|
-
},
|
|
603
|
-
{
|
|
604
|
-
"name": "topolo-consent-api",
|
|
605
|
-
"kind": "worker",
|
|
606
|
-
"apiUrl": null,
|
|
607
|
-
"domains": [],
|
|
608
|
-
"routes": [],
|
|
609
|
-
"appId": null
|
|
610
|
-
}
|
|
611
|
-
]
|
|
612
|
-
},
|
|
613
|
-
"crm": {
|
|
614
|
-
"id": "crm",
|
|
615
|
-
"appDir": "TopoloCRM",
|
|
616
|
-
"name": "topolo-crm",
|
|
617
|
-
"packageName": "topolo-crm",
|
|
618
|
-
"productionUrl": "https://crm.topolo.app",
|
|
619
|
-
"services": [
|
|
620
|
-
"crm"
|
|
621
|
-
],
|
|
622
|
-
"deployTargets": [
|
|
623
|
-
{
|
|
624
|
-
"name": "topolo-crm-api",
|
|
625
|
-
"kind": "worker",
|
|
626
|
-
"apiUrl": null,
|
|
627
|
-
"domains": [],
|
|
628
|
-
"routes": [],
|
|
629
|
-
"appId": null
|
|
630
|
-
},
|
|
631
|
-
{
|
|
632
|
-
"name": "topolo-crm-app",
|
|
633
|
-
"kind": "worker",
|
|
634
|
-
"apiUrl": "https://crm.topolo.app",
|
|
635
|
-
"domains": [
|
|
636
|
-
"crm.topolo.app"
|
|
637
|
-
],
|
|
638
|
-
"routes": [
|
|
639
|
-
"crm.topolo.app"
|
|
640
|
-
],
|
|
641
|
-
"appId": "crm"
|
|
642
|
-
}
|
|
643
|
-
]
|
|
644
|
-
},
|
|
645
|
-
"design": {
|
|
646
|
-
"id": "design",
|
|
647
|
-
"appDir": "TopoloDesign",
|
|
648
|
-
"name": "topolo-design",
|
|
649
|
-
"packageName": "@topolo-io/design",
|
|
650
|
-
"productionUrl": "https://design.topolo.app",
|
|
651
|
-
"services": [
|
|
652
|
-
"design"
|
|
653
|
-
],
|
|
654
|
-
"deployTargets": [
|
|
655
|
-
{
|
|
656
|
-
"name": "topolo-design",
|
|
657
|
-
"kind": "worker",
|
|
658
|
-
"apiUrl": "https://design.topolo.app",
|
|
659
|
-
"domains": [
|
|
660
|
-
"design.topolo.app"
|
|
661
|
-
],
|
|
662
|
-
"routes": [
|
|
663
|
-
"design.topolo.app"
|
|
664
|
-
],
|
|
665
|
-
"appId": "design"
|
|
666
|
-
},
|
|
667
|
-
{
|
|
668
|
-
"name": "topolo-design-api",
|
|
669
|
-
"kind": "worker",
|
|
670
|
-
"apiUrl": null,
|
|
671
|
-
"domains": [],
|
|
672
|
-
"routes": [],
|
|
673
|
-
"appId": null
|
|
674
|
-
}
|
|
675
|
-
]
|
|
676
|
-
},
|
|
677
|
-
"developers": {
|
|
678
|
-
"id": "developers",
|
|
679
|
-
"appDir": "TopoloDevelopers",
|
|
680
|
-
"name": "topolo-developers",
|
|
681
|
-
"packageName": "topolo-developers",
|
|
682
|
-
"productionUrl": "https://developers.topolo.app",
|
|
683
|
-
"services": [
|
|
684
|
-
"developers"
|
|
685
|
-
],
|
|
686
|
-
"deployTargets": [
|
|
687
|
-
{
|
|
688
|
-
"name": "topolo-catalog-cron",
|
|
689
|
-
"kind": "worker",
|
|
690
|
-
"apiUrl": null,
|
|
691
|
-
"domains": [],
|
|
692
|
-
"routes": [],
|
|
693
|
-
"appId": null
|
|
694
|
-
},
|
|
695
|
-
{
|
|
696
|
-
"name": "topolo-developers",
|
|
697
|
-
"kind": "worker",
|
|
698
|
-
"apiUrl": "https://developers.topolo.app",
|
|
699
|
-
"domains": [
|
|
700
|
-
"developers.topolo.app"
|
|
701
|
-
],
|
|
702
|
-
"routes": [
|
|
703
|
-
"developers.topolo.app"
|
|
704
|
-
],
|
|
705
|
-
"appId": "developers"
|
|
706
|
-
},
|
|
707
|
-
{
|
|
708
|
-
"name": "topolo-developers-api",
|
|
709
|
-
"kind": "worker",
|
|
710
|
-
"apiUrl": null,
|
|
711
|
-
"domains": [],
|
|
712
|
-
"routes": [],
|
|
713
|
-
"appId": null
|
|
714
|
-
}
|
|
715
|
-
]
|
|
716
|
-
},
|
|
717
|
-
"director": {
|
|
718
|
-
"id": "director",
|
|
719
|
-
"appDir": "TopoloDirector",
|
|
720
|
-
"name": "topolo-director",
|
|
721
|
-
"packageName": "topolo-director",
|
|
722
|
-
"productionUrl": "https://director.topolo.app",
|
|
723
|
-
"services": [
|
|
724
|
-
"director"
|
|
725
|
-
],
|
|
726
|
-
"deployTargets": [
|
|
727
|
-
{
|
|
728
|
-
"name": "topolo-director",
|
|
729
|
-
"kind": "worker",
|
|
730
|
-
"apiUrl": "https://director.topolo.app",
|
|
731
|
-
"domains": [
|
|
732
|
-
"director.topolo.app"
|
|
733
|
-
],
|
|
734
|
-
"routes": [
|
|
735
|
-
"director.topolo.app"
|
|
736
|
-
],
|
|
737
|
-
"appId": "director"
|
|
738
|
-
},
|
|
739
|
-
{
|
|
740
|
-
"name": "topolo-director-api",
|
|
741
|
-
"kind": "worker",
|
|
742
|
-
"apiUrl": null,
|
|
743
|
-
"domains": [],
|
|
744
|
-
"routes": [],
|
|
745
|
-
"appId": null
|
|
746
|
-
}
|
|
747
|
-
]
|
|
748
|
-
},
|
|
749
|
-
"docs": {
|
|
750
|
-
"id": "docs",
|
|
751
|
-
"appDir": "TopoloDocs",
|
|
752
|
-
"name": "topolo-docs",
|
|
753
|
-
"packageName": "topolo-docs",
|
|
754
|
-
"productionUrl": "https://docs.topolo.app",
|
|
755
|
-
"services": [],
|
|
756
|
-
"deployTargets": [
|
|
757
|
-
{
|
|
758
|
-
"name": "topolo-docs",
|
|
759
|
-
"kind": "worker",
|
|
760
|
-
"apiUrl": "https://docs.topolo.app",
|
|
761
|
-
"domains": [
|
|
762
|
-
"docs.topolo.app"
|
|
763
|
-
],
|
|
764
|
-
"routes": [
|
|
765
|
-
"docs.topolo.app"
|
|
766
|
-
],
|
|
767
|
-
"appId": null
|
|
768
|
-
},
|
|
769
|
-
{
|
|
770
|
-
"name": "topolo-docs-api",
|
|
771
|
-
"kind": "worker",
|
|
772
|
-
"apiUrl": null,
|
|
773
|
-
"domains": [],
|
|
774
|
-
"routes": [],
|
|
775
|
-
"appId": null
|
|
776
|
-
}
|
|
777
|
-
]
|
|
778
|
-
},
|
|
779
|
-
"feed": {
|
|
780
|
-
"id": "feed",
|
|
781
|
-
"appDir": "TopoloFeed",
|
|
782
|
-
"name": "TopoloFeed",
|
|
783
|
-
"packageName": "topolo-feed",
|
|
784
|
-
"productionUrl": "https://feed.topolo.app",
|
|
785
|
-
"services": [
|
|
786
|
-
"feed"
|
|
787
|
-
],
|
|
788
|
-
"deployTargets": [
|
|
789
|
-
{
|
|
790
|
-
"name": "topolo-feed-analytics-api",
|
|
791
|
-
"kind": "worker",
|
|
792
|
-
"apiUrl": "https://feed-analytics-api.topolo.app",
|
|
793
|
-
"domains": [
|
|
794
|
-
"feed-analytics-api.topolo.app"
|
|
795
|
-
],
|
|
796
|
-
"routes": [
|
|
797
|
-
"feed-analytics-api.topolo.app"
|
|
798
|
-
],
|
|
799
|
-
"appId": null
|
|
800
|
-
},
|
|
801
|
-
{
|
|
802
|
-
"name": "topolo-feed-api",
|
|
803
|
-
"kind": "worker",
|
|
804
|
-
"apiUrl": "https://feed-api.topolo.app",
|
|
805
|
-
"domains": [
|
|
806
|
-
"feed-api.topolo.app"
|
|
807
|
-
],
|
|
808
|
-
"routes": [
|
|
809
|
-
"feed-api.topolo.app"
|
|
810
|
-
],
|
|
811
|
-
"appId": null
|
|
812
|
-
},
|
|
813
|
-
{
|
|
814
|
-
"name": "topolo-feed-web",
|
|
815
|
-
"kind": "worker",
|
|
816
|
-
"apiUrl": "https://feed.topolo.app",
|
|
817
|
-
"domains": [
|
|
818
|
-
"feed.topolo.app"
|
|
819
|
-
],
|
|
820
|
-
"routes": [
|
|
821
|
-
"feed.topolo.app"
|
|
822
|
-
],
|
|
823
|
-
"appId": "feed"
|
|
824
|
-
}
|
|
825
|
-
]
|
|
826
|
-
},
|
|
827
|
-
"flow": {
|
|
828
|
-
"id": "flow",
|
|
829
|
-
"appDir": "TopoloFlow",
|
|
830
|
-
"name": "topolo-flow",
|
|
831
|
-
"packageName": "topolo-flow",
|
|
832
|
-
"productionUrl": "https://flow.topolo.app",
|
|
833
|
-
"services": [
|
|
834
|
-
"flow"
|
|
835
|
-
],
|
|
836
|
-
"deployTargets": [
|
|
837
|
-
{
|
|
838
|
-
"name": "topolo-flow",
|
|
839
|
-
"kind": "worker",
|
|
840
|
-
"apiUrl": "https://flow.topolo.app",
|
|
841
|
-
"domains": [
|
|
842
|
-
"flow.topolo.app"
|
|
843
|
-
],
|
|
844
|
-
"routes": [
|
|
845
|
-
"flow.topolo.app"
|
|
846
|
-
],
|
|
847
|
-
"appId": "flow"
|
|
848
|
-
},
|
|
849
|
-
{
|
|
850
|
-
"name": "topolo-flow-api",
|
|
851
|
-
"kind": "worker",
|
|
852
|
-
"apiUrl": null,
|
|
853
|
-
"domains": [],
|
|
854
|
-
"routes": [],
|
|
855
|
-
"appId": null
|
|
856
|
-
}
|
|
857
|
-
]
|
|
858
|
-
},
|
|
859
|
-
"forecast": {
|
|
860
|
-
"id": "forecast",
|
|
861
|
-
"appDir": "TopoloForecast",
|
|
862
|
-
"name": "topolo-forecast",
|
|
863
|
-
"packageName": "topolo-forecast",
|
|
864
|
-
"productionUrl": "https://forecast.topolo.app",
|
|
865
|
-
"services": [
|
|
866
|
-
"forecast"
|
|
867
|
-
],
|
|
868
|
-
"deployTargets": [
|
|
869
|
-
{
|
|
870
|
-
"name": "topolo-forecast",
|
|
871
|
-
"kind": "worker",
|
|
872
|
-
"apiUrl": "https://forecast.topolo.app",
|
|
873
|
-
"domains": [
|
|
874
|
-
"forecast.topolo.app"
|
|
875
|
-
],
|
|
876
|
-
"routes": [
|
|
877
|
-
"forecast.topolo.app"
|
|
878
|
-
],
|
|
879
|
-
"appId": "forecast"
|
|
880
|
-
},
|
|
881
|
-
{
|
|
882
|
-
"name": "topolo-forecast-api",
|
|
883
|
-
"kind": "worker",
|
|
884
|
-
"apiUrl": null,
|
|
885
|
-
"domains": [],
|
|
886
|
-
"routes": [],
|
|
887
|
-
"appId": null
|
|
888
|
-
}
|
|
889
|
-
]
|
|
890
|
-
},
|
|
891
|
-
"forms": {
|
|
892
|
-
"id": "forms",
|
|
893
|
-
"appDir": "TopoloForms",
|
|
894
|
-
"name": "topolo-forms",
|
|
895
|
-
"packageName": "topolo-forms",
|
|
896
|
-
"productionUrl": "https://forms.topolo.app",
|
|
897
|
-
"services": [
|
|
898
|
-
"forms"
|
|
899
|
-
],
|
|
900
|
-
"deployTargets": [
|
|
901
|
-
{
|
|
902
|
-
"name": "topolo-forms",
|
|
903
|
-
"kind": "worker",
|
|
904
|
-
"apiUrl": "https://forms.topolo.app",
|
|
905
|
-
"domains": [
|
|
906
|
-
"forms.topolo.app"
|
|
907
|
-
],
|
|
908
|
-
"routes": [
|
|
909
|
-
"forms.topolo.app"
|
|
910
|
-
],
|
|
911
|
-
"appId": "forms"
|
|
912
|
-
},
|
|
913
|
-
{
|
|
914
|
-
"name": "topolo-forms-api",
|
|
915
|
-
"kind": "worker",
|
|
916
|
-
"apiUrl": null,
|
|
917
|
-
"domains": [],
|
|
918
|
-
"routes": [],
|
|
919
|
-
"appId": null
|
|
920
|
-
}
|
|
921
|
-
]
|
|
922
|
-
},
|
|
923
|
-
"home": {
|
|
924
|
-
"id": "home",
|
|
925
|
-
"appDir": "TopoloHome",
|
|
926
|
-
"name": "topolo-home",
|
|
927
|
-
"packageName": "topolo-home",
|
|
928
|
-
"productionUrl": "https://home.topolo.app",
|
|
929
|
-
"services": [
|
|
930
|
-
"home"
|
|
931
|
-
],
|
|
932
|
-
"deployTargets": [
|
|
933
|
-
{
|
|
934
|
-
"name": "topolo-home",
|
|
935
|
-
"kind": "worker",
|
|
936
|
-
"apiUrl": "https://home.topolo.app",
|
|
937
|
-
"domains": [
|
|
938
|
-
"home.topolo.app"
|
|
939
|
-
],
|
|
940
|
-
"routes": [
|
|
941
|
-
"home.topolo.app"
|
|
942
|
-
],
|
|
943
|
-
"appId": "home"
|
|
944
|
-
},
|
|
945
|
-
{
|
|
946
|
-
"name": "topolo-home-api",
|
|
947
|
-
"kind": "worker",
|
|
948
|
-
"apiUrl": null,
|
|
949
|
-
"domains": [],
|
|
950
|
-
"routes": [],
|
|
951
|
-
"appId": null
|
|
952
|
-
}
|
|
953
|
-
]
|
|
954
|
-
},
|
|
955
|
-
"insights": {
|
|
956
|
-
"id": "insights",
|
|
957
|
-
"appDir": "TopoloInsights",
|
|
958
|
-
"name": "topolo-insights",
|
|
959
|
-
"packageName": "topolo-insights",
|
|
960
|
-
"productionUrl": "https://insights.topolo.app",
|
|
961
|
-
"services": [
|
|
962
|
-
"insights"
|
|
963
|
-
],
|
|
964
|
-
"deployTargets": [
|
|
965
|
-
{
|
|
966
|
-
"name": "topolo-insights",
|
|
967
|
-
"kind": "worker",
|
|
968
|
-
"apiUrl": "https://insights.topolo.app",
|
|
969
|
-
"domains": [
|
|
970
|
-
"insights.topolo.app"
|
|
971
|
-
],
|
|
972
|
-
"routes": [
|
|
973
|
-
"insights.topolo.app"
|
|
974
|
-
],
|
|
975
|
-
"appId": "insights"
|
|
976
|
-
},
|
|
977
|
-
{
|
|
978
|
-
"name": "topolo-insights-api",
|
|
979
|
-
"kind": "worker",
|
|
980
|
-
"apiUrl": null,
|
|
981
|
-
"domains": [],
|
|
982
|
-
"routes": [],
|
|
983
|
-
"appId": null
|
|
984
|
-
}
|
|
985
|
-
]
|
|
986
|
-
},
|
|
987
|
-
"inventory": {
|
|
988
|
-
"id": "inventory",
|
|
989
|
-
"appDir": "TopoloInventory",
|
|
990
|
-
"name": "topolo-inventory",
|
|
991
|
-
"packageName": "topolo-inventory",
|
|
992
|
-
"productionUrl": "https://inventory.topolo.app",
|
|
993
|
-
"services": [
|
|
994
|
-
"inventory"
|
|
995
|
-
],
|
|
996
|
-
"deployTargets": [
|
|
997
|
-
{
|
|
998
|
-
"name": "topolo-inventory",
|
|
999
|
-
"kind": "worker",
|
|
1000
|
-
"apiUrl": "https://inventory.topolo.app",
|
|
1001
|
-
"domains": [
|
|
1002
|
-
"inventory.topolo.app"
|
|
1003
|
-
],
|
|
1004
|
-
"routes": [
|
|
1005
|
-
"inventory.topolo.app"
|
|
1006
|
-
],
|
|
1007
|
-
"appId": "inventory"
|
|
1008
|
-
},
|
|
1009
|
-
{
|
|
1010
|
-
"name": "topolo-inventory-api",
|
|
1011
|
-
"kind": "worker",
|
|
1012
|
-
"apiUrl": null,
|
|
1013
|
-
"domains": [],
|
|
1014
|
-
"routes": [],
|
|
1015
|
-
"appId": null
|
|
1016
|
-
}
|
|
1017
|
-
]
|
|
1018
|
-
},
|
|
1019
|
-
"learn": {
|
|
1020
|
-
"id": "learn",
|
|
1021
|
-
"appDir": "TopoloLearn",
|
|
1022
|
-
"name": "topololearn",
|
|
1023
|
-
"packageName": "topolo-learn",
|
|
1024
|
-
"productionUrl": "https://learn.topolo.app",
|
|
1025
|
-
"services": [
|
|
1026
|
-
"learn"
|
|
1027
|
-
],
|
|
1028
|
-
"deployTargets": [
|
|
1029
|
-
{
|
|
1030
|
-
"name": "topolo-learn",
|
|
1031
|
-
"kind": "worker",
|
|
1032
|
-
"apiUrl": "https://learn.topolo.app",
|
|
1033
|
-
"domains": [
|
|
1034
|
-
"learn.topolo.app"
|
|
1035
|
-
],
|
|
1036
|
-
"routes": [
|
|
1037
|
-
"learn.topolo.app"
|
|
1038
|
-
],
|
|
1039
|
-
"appId": "learn"
|
|
1040
|
-
},
|
|
1041
|
-
{
|
|
1042
|
-
"name": "topolo-learn-api",
|
|
1043
|
-
"kind": "worker",
|
|
1044
|
-
"apiUrl": null,
|
|
1045
|
-
"domains": [],
|
|
1046
|
-
"routes": [],
|
|
1047
|
-
"appId": null
|
|
1048
|
-
}
|
|
1049
|
-
]
|
|
1050
|
-
},
|
|
1051
|
-
"localize": {
|
|
1052
|
-
"id": "localize",
|
|
1053
|
-
"appDir": "TopoloLocalize",
|
|
1054
|
-
"name": "topolo-localize",
|
|
1055
|
-
"packageName": "topolo-localize",
|
|
1056
|
-
"productionUrl": "https://localize.topolo.app",
|
|
1057
|
-
"services": [
|
|
1058
|
-
"localize"
|
|
1059
|
-
],
|
|
1060
|
-
"deployTargets": [
|
|
1061
|
-
{
|
|
1062
|
-
"name": "topolo-localize",
|
|
1063
|
-
"kind": "worker",
|
|
1064
|
-
"apiUrl": "https://localize.topolo.app",
|
|
1065
|
-
"domains": [
|
|
1066
|
-
"localize.topolo.app"
|
|
1067
|
-
],
|
|
1068
|
-
"routes": [
|
|
1069
|
-
"localize.topolo.app"
|
|
1070
|
-
],
|
|
1071
|
-
"appId": "localize"
|
|
1072
|
-
}
|
|
1073
|
-
]
|
|
1074
|
-
},
|
|
1075
|
-
"mail": {
|
|
1076
|
-
"id": "mail",
|
|
1077
|
-
"appDir": "TopoloMail",
|
|
1078
|
-
"name": "topolo-mail",
|
|
1079
|
-
"packageName": "topolo-mail",
|
|
1080
|
-
"productionUrl": "https://mail.topolo.app",
|
|
1081
|
-
"services": [
|
|
1082
|
-
"mail"
|
|
1083
|
-
],
|
|
1084
|
-
"deployTargets": [
|
|
1085
|
-
{
|
|
1086
|
-
"name": "topolo-mail",
|
|
1087
|
-
"kind": "worker",
|
|
1088
|
-
"apiUrl": "https://mail.topolo.app",
|
|
1089
|
-
"domains": [
|
|
1090
|
-
"mail.topolo.app"
|
|
1091
|
-
],
|
|
1092
|
-
"routes": [
|
|
1093
|
-
"mail.topolo.app"
|
|
1094
|
-
],
|
|
1095
|
-
"appId": "mail"
|
|
1096
|
-
},
|
|
1097
|
-
{
|
|
1098
|
-
"name": "topolo-mail-api",
|
|
1099
|
-
"kind": "worker",
|
|
1100
|
-
"apiUrl": null,
|
|
1101
|
-
"domains": [],
|
|
1102
|
-
"routes": [],
|
|
1103
|
-
"appId": null
|
|
1104
|
-
}
|
|
1105
|
-
]
|
|
1106
|
-
},
|
|
1107
|
-
"mdm": {
|
|
1108
|
-
"id": "mdm",
|
|
1109
|
-
"appDir": "TopoloMDM",
|
|
1110
|
-
"name": "TopoloMDM",
|
|
1111
|
-
"packageName": "topolo-mdm",
|
|
1112
|
-
"productionUrl": "https://mdm.topolo.app",
|
|
1113
|
-
"services": [
|
|
1114
|
-
"mdm"
|
|
1115
|
-
],
|
|
1116
|
-
"deployTargets": [
|
|
1117
|
-
{
|
|
1118
|
-
"name": "topolo-mdm-api",
|
|
1119
|
-
"kind": "worker",
|
|
1120
|
-
"apiUrl": null,
|
|
1121
|
-
"domains": [],
|
|
1122
|
-
"routes": [],
|
|
1123
|
-
"appId": null
|
|
1124
|
-
},
|
|
1125
|
-
{
|
|
1126
|
-
"name": "topolo-mdm-console",
|
|
1127
|
-
"kind": "worker",
|
|
1128
|
-
"apiUrl": "https://mdm.topolo.app",
|
|
1129
|
-
"domains": [
|
|
1130
|
-
"mdm.topolo.app"
|
|
1131
|
-
],
|
|
1132
|
-
"routes": [
|
|
1133
|
-
"mdm.topolo.app"
|
|
1134
|
-
],
|
|
1135
|
-
"appId": "mdm"
|
|
1136
|
-
}
|
|
1137
|
-
]
|
|
1138
|
-
},
|
|
1139
|
-
"messages": {
|
|
1140
|
-
"id": "messages",
|
|
1141
|
-
"appDir": "TopoloMessages",
|
|
1142
|
-
"name": "topolo-messages",
|
|
1143
|
-
"packageName": "topolo-messages",
|
|
1144
|
-
"productionUrl": "https://messages.topolo.app",
|
|
1145
|
-
"services": [
|
|
1146
|
-
"messages"
|
|
1147
|
-
],
|
|
1148
|
-
"deployTargets": [
|
|
1149
|
-
{
|
|
1150
|
-
"name": "topolo-messages",
|
|
1151
|
-
"kind": "worker",
|
|
1152
|
-
"apiUrl": "https://messages.topolo.app",
|
|
1153
|
-
"domains": [
|
|
1154
|
-
"messages.topolo.app"
|
|
1155
|
-
],
|
|
1156
|
-
"routes": [
|
|
1157
|
-
"messages.topolo.app"
|
|
1158
|
-
],
|
|
1159
|
-
"appId": "messages"
|
|
1160
|
-
},
|
|
1161
|
-
{
|
|
1162
|
-
"name": "topolo-messages-api",
|
|
1163
|
-
"kind": "worker",
|
|
1164
|
-
"apiUrl": null,
|
|
1165
|
-
"domains": [],
|
|
1166
|
-
"routes": [],
|
|
1167
|
-
"appId": null
|
|
1168
|
-
},
|
|
1169
|
-
{
|
|
1170
|
-
"name": "topolo-messages-dispatch",
|
|
1171
|
-
"kind": "worker",
|
|
1172
|
-
"apiUrl": null,
|
|
1173
|
-
"domains": [],
|
|
1174
|
-
"routes": [],
|
|
1175
|
-
"appId": null
|
|
1176
|
-
}
|
|
1177
|
-
]
|
|
1178
|
-
},
|
|
1179
|
-
"nexus": {
|
|
1180
|
-
"id": "nexus",
|
|
1181
|
-
"appDir": "TopoloNexus",
|
|
1182
|
-
"name": "topolo-nexus",
|
|
1183
|
-
"packageName": "topolo-nexus",
|
|
1184
|
-
"productionUrl": "https://nexus.topolo.app",
|
|
1185
|
-
"services": [
|
|
1186
|
-
"nexus"
|
|
1187
|
-
],
|
|
1188
|
-
"deployTargets": [
|
|
1189
|
-
{
|
|
1190
|
-
"name": "topolo-nexus-dashboard",
|
|
1191
|
-
"kind": "worker",
|
|
1192
|
-
"apiUrl": "https://nexus.topolo.app",
|
|
1193
|
-
"domains": [
|
|
1194
|
-
"nexus.topolo.app"
|
|
1195
|
-
],
|
|
1196
|
-
"routes": [
|
|
1197
|
-
"nexus.topolo.app"
|
|
1198
|
-
],
|
|
1199
|
-
"appId": null
|
|
1200
|
-
},
|
|
1201
|
-
{
|
|
1202
|
-
"name": "topolo-nexus-gateway",
|
|
1203
|
-
"kind": "worker",
|
|
1204
|
-
"apiUrl": "https://nexus.topolo.app",
|
|
1205
|
-
"domains": [
|
|
1206
|
-
"nexus.topolo.app"
|
|
1207
|
-
],
|
|
1208
|
-
"routes": [],
|
|
1209
|
-
"appId": "nexus"
|
|
1210
|
-
}
|
|
1211
|
-
]
|
|
1212
|
-
},
|
|
1213
|
-
"notify": {
|
|
1214
|
-
"id": "notify",
|
|
1215
|
-
"appDir": "TopoloNotify",
|
|
1216
|
-
"name": "topolo-notify",
|
|
1217
|
-
"packageName": "@topolo/notify",
|
|
1218
|
-
"productionUrl": "https://notify.topolo.app",
|
|
1219
|
-
"services": [
|
|
1220
|
-
"notify"
|
|
1221
|
-
],
|
|
1222
|
-
"deployTargets": [
|
|
1223
|
-
{
|
|
1224
|
-
"name": "topolo-notify",
|
|
1225
|
-
"kind": "worker",
|
|
1226
|
-
"apiUrl": "https://notify.topolo.app",
|
|
1227
|
-
"domains": [
|
|
1228
|
-
"notify.topolo.app"
|
|
1229
|
-
],
|
|
1230
|
-
"routes": [
|
|
1231
|
-
"notify.topolo.app"
|
|
1232
|
-
],
|
|
1233
|
-
"appId": "notify"
|
|
1234
|
-
}
|
|
1235
|
-
]
|
|
1236
|
-
},
|
|
1237
|
-
"observability_watch": {
|
|
1238
|
-
"id": "observability_watch",
|
|
1239
|
-
"appDir": "TopoloObservabilityWatch",
|
|
1240
|
-
"name": "topolo-observability-watch",
|
|
1241
|
-
"packageName": "topolo-observability-watch",
|
|
1242
|
-
"productionUrl": "https://observability-watch.topolo.app",
|
|
1243
|
-
"services": [
|
|
1244
|
-
"observability_watch"
|
|
1245
|
-
],
|
|
1246
|
-
"deployTargets": [
|
|
1247
|
-
{
|
|
1248
|
-
"name": "topolo-observability-watch",
|
|
1249
|
-
"kind": "worker",
|
|
1250
|
-
"apiUrl": "https://observability-watch.topolo.app",
|
|
1251
|
-
"domains": [
|
|
1252
|
-
"observability-watch.topolo.app"
|
|
1253
|
-
],
|
|
1254
|
-
"routes": [
|
|
1255
|
-
"observability-watch.topolo.app"
|
|
1256
|
-
],
|
|
1257
|
-
"appId": "observability_watch"
|
|
1258
|
-
}
|
|
1259
|
-
]
|
|
1260
|
-
},
|
|
1261
|
-
"one": {
|
|
1262
|
-
"id": "one",
|
|
1263
|
-
"appDir": "TopoloOne",
|
|
1264
|
-
"name": "TopoloOne",
|
|
1265
|
-
"packageName": "topolo-oneclick-sso",
|
|
1266
|
-
"productionUrl": "https://one.topolo.app",
|
|
1267
|
-
"services": [
|
|
1268
|
-
"one"
|
|
1269
|
-
],
|
|
1270
|
-
"deployTargets": [
|
|
1271
|
-
{
|
|
1272
|
-
"name": "topolo-one",
|
|
1273
|
-
"kind": "worker",
|
|
1274
|
-
"apiUrl": "https://one.topolo.app",
|
|
1275
|
-
"domains": [
|
|
1276
|
-
"one.topolo.app"
|
|
1277
|
-
],
|
|
1278
|
-
"routes": [
|
|
1279
|
-
"one.topolo.app/*"
|
|
1280
|
-
],
|
|
1281
|
-
"appId": null
|
|
1282
|
-
},
|
|
1283
|
-
{
|
|
1284
|
-
"name": "topolo-one-api",
|
|
1285
|
-
"kind": "worker",
|
|
1286
|
-
"apiUrl": "https://www.topolo.app",
|
|
1287
|
-
"domains": [
|
|
1288
|
-
"www.topolo.app"
|
|
1289
|
-
],
|
|
1290
|
-
"routes": [
|
|
1291
|
-
"www.topolo.app/*"
|
|
1292
|
-
],
|
|
1293
|
-
"appId": "one"
|
|
1294
|
-
},
|
|
1295
|
-
{
|
|
1296
|
-
"name": "topolo-one-website",
|
|
1297
|
-
"kind": "worker",
|
|
1298
|
-
"apiUrl": "https://topolo.io",
|
|
1299
|
-
"domains": [
|
|
1300
|
-
"topolo.io"
|
|
1301
|
-
],
|
|
1302
|
-
"routes": [
|
|
1303
|
-
"one.topolo.io",
|
|
1304
|
-
"topolo.io",
|
|
1305
|
-
"www.topolo.io"
|
|
1306
|
-
],
|
|
1307
|
-
"appId": null
|
|
1308
|
-
}
|
|
1309
|
-
]
|
|
1310
|
-
},
|
|
1311
|
-
"p2p": {
|
|
1312
|
-
"id": "p2p",
|
|
1313
|
-
"appDir": "TopoloP2P",
|
|
1314
|
-
"name": "topolo-p2p",
|
|
1315
|
-
"packageName": "topolo-p2p",
|
|
1316
|
-
"productionUrl": "https://p2p.topolo.app",
|
|
1317
|
-
"services": [
|
|
1318
|
-
"p2p"
|
|
1319
|
-
],
|
|
1320
|
-
"deployTargets": [
|
|
1321
|
-
{
|
|
1322
|
-
"name": "topolo-p2p",
|
|
1323
|
-
"kind": "worker",
|
|
1324
|
-
"apiUrl": "https://p2p.topolo.app",
|
|
1325
|
-
"domains": [
|
|
1326
|
-
"p2p.topolo.app"
|
|
1327
|
-
],
|
|
1328
|
-
"routes": [
|
|
1329
|
-
"p2p.topolo.app"
|
|
1330
|
-
],
|
|
1331
|
-
"appId": "p2p"
|
|
1332
|
-
},
|
|
1333
|
-
{
|
|
1334
|
-
"name": "topolo-p2p-api",
|
|
1335
|
-
"kind": "worker",
|
|
1336
|
-
"apiUrl": null,
|
|
1337
|
-
"domains": [],
|
|
1338
|
-
"routes": [],
|
|
1339
|
-
"appId": null
|
|
1340
|
-
}
|
|
1341
|
-
]
|
|
1342
|
-
},
|
|
1343
|
-
"pay": {
|
|
1344
|
-
"id": "pay",
|
|
1345
|
-
"appDir": "TopoloPay",
|
|
1346
|
-
"name": "topolo-pay",
|
|
1347
|
-
"packageName": "topolo-pay",
|
|
1348
|
-
"productionUrl": "https://pay.topolo.app",
|
|
1349
|
-
"services": [
|
|
1350
|
-
"pay"
|
|
1351
|
-
],
|
|
1352
|
-
"deployTargets": [
|
|
1353
|
-
{
|
|
1354
|
-
"name": "topolo-pay",
|
|
1355
|
-
"kind": "worker",
|
|
1356
|
-
"apiUrl": "https://pay.topolo.app",
|
|
1357
|
-
"domains": [
|
|
1358
|
-
"pay.topolo.app"
|
|
1359
|
-
],
|
|
1360
|
-
"routes": [
|
|
1361
|
-
"pay.topolo.app"
|
|
1362
|
-
],
|
|
1363
|
-
"appId": "pay"
|
|
1364
|
-
},
|
|
1365
|
-
{
|
|
1366
|
-
"name": "topolo-pay-api",
|
|
1367
|
-
"kind": "worker",
|
|
1368
|
-
"apiUrl": null,
|
|
1369
|
-
"domains": [],
|
|
1370
|
-
"routes": [],
|
|
1371
|
-
"appId": null
|
|
1372
|
-
}
|
|
1373
|
-
]
|
|
1374
|
-
},
|
|
1375
|
-
"people": {
|
|
1376
|
-
"id": "people",
|
|
1377
|
-
"appDir": "TopoloPeople",
|
|
1378
|
-
"name": "topolo-people",
|
|
1379
|
-
"packageName": "topolo-people",
|
|
1380
|
-
"productionUrl": "https://people.topolo.app",
|
|
1381
|
-
"services": [
|
|
1382
|
-
"people"
|
|
1383
|
-
],
|
|
1384
|
-
"deployTargets": [
|
|
1385
|
-
{
|
|
1386
|
-
"name": "topolo-people",
|
|
1387
|
-
"kind": "worker",
|
|
1388
|
-
"apiUrl": "https://people.topolo.app",
|
|
1389
|
-
"domains": [
|
|
1390
|
-
"people.topolo.app"
|
|
1391
|
-
],
|
|
1392
|
-
"routes": [
|
|
1393
|
-
"people.topolo.app"
|
|
1394
|
-
],
|
|
1395
|
-
"appId": "people"
|
|
1396
|
-
},
|
|
1397
|
-
{
|
|
1398
|
-
"name": "topolo-people-api",
|
|
1399
|
-
"kind": "worker",
|
|
1400
|
-
"apiUrl": null,
|
|
1401
|
-
"domains": [],
|
|
1402
|
-
"routes": [],
|
|
1403
|
-
"appId": null
|
|
1404
|
-
}
|
|
1405
|
-
]
|
|
1406
|
-
},
|
|
1407
|
-
"quro": {
|
|
1408
|
-
"id": "quro",
|
|
1409
|
-
"appDir": "TopoloQuro",
|
|
1410
|
-
"name": "TopoloQuro",
|
|
1411
|
-
"packageName": "topolo-quro",
|
|
1412
|
-
"productionUrl": "https://quro.topolo.app",
|
|
1413
|
-
"services": [
|
|
1414
|
-
"quro.api",
|
|
1415
|
-
"quro.redirect"
|
|
1416
|
-
],
|
|
1417
|
-
"deployTargets": [
|
|
1418
|
-
{
|
|
1419
|
-
"name": "quro-api",
|
|
1420
|
-
"kind": "worker",
|
|
1421
|
-
"apiUrl": "https://api.ol0.me",
|
|
1422
|
-
"domains": [
|
|
1423
|
-
"api.ol0.me"
|
|
1424
|
-
],
|
|
1425
|
-
"routes": [
|
|
1426
|
-
"api.ol0.me/*"
|
|
1427
|
-
],
|
|
1428
|
-
"appId": "quro.api"
|
|
1429
|
-
},
|
|
1430
|
-
{
|
|
1431
|
-
"name": "quro-redirect-system",
|
|
1432
|
-
"kind": "worker",
|
|
1433
|
-
"apiUrl": "https://ol0.me",
|
|
1434
|
-
"domains": [
|
|
1435
|
-
"ol0.me"
|
|
1436
|
-
],
|
|
1437
|
-
"routes": [
|
|
1438
|
-
"ol0.me/*"
|
|
1439
|
-
],
|
|
1440
|
-
"appId": "quro.redirect"
|
|
1441
|
-
},
|
|
1442
|
-
{
|
|
1443
|
-
"name": "quro-ui",
|
|
1444
|
-
"kind": "worker",
|
|
1445
|
-
"apiUrl": "https://quro.topolo.app",
|
|
1446
|
-
"domains": [
|
|
1447
|
-
"quro.topolo.app"
|
|
1448
|
-
],
|
|
1449
|
-
"routes": [
|
|
1450
|
-
"quro.topolo.app"
|
|
1451
|
-
],
|
|
1452
|
-
"appId": null
|
|
1453
|
-
}
|
|
1454
|
-
]
|
|
1455
|
-
},
|
|
1456
|
-
"roadmapper": {
|
|
1457
|
-
"id": "roadmapper",
|
|
1458
|
-
"appDir": "TopoloRoadmapper",
|
|
1459
|
-
"name": "topolo-roadmapper",
|
|
1460
|
-
"packageName": "topolo-roadmapper",
|
|
1461
|
-
"productionUrl": "https://roadmapper.topolo.app",
|
|
1462
|
-
"services": [
|
|
1463
|
-
"roadmapper"
|
|
1464
|
-
],
|
|
1465
|
-
"deployTargets": [
|
|
1466
|
-
{
|
|
1467
|
-
"name": "topolo-roadmapper",
|
|
1468
|
-
"kind": "worker",
|
|
1469
|
-
"apiUrl": "https://roadmapper.topolo.app",
|
|
1470
|
-
"domains": [
|
|
1471
|
-
"roadmapper.topolo.app"
|
|
1472
|
-
],
|
|
1473
|
-
"routes": [
|
|
1474
|
-
"roadmapper.topolo.app"
|
|
1475
|
-
],
|
|
1476
|
-
"appId": "roadmapper"
|
|
1477
|
-
},
|
|
1478
|
-
{
|
|
1479
|
-
"name": "topolo-roadmapper-api",
|
|
1480
|
-
"kind": "worker",
|
|
1481
|
-
"apiUrl": null,
|
|
1482
|
-
"domains": [],
|
|
1483
|
-
"routes": [],
|
|
1484
|
-
"appId": null
|
|
1485
|
-
}
|
|
1486
|
-
]
|
|
1487
|
-
},
|
|
1488
|
-
"sign": {
|
|
1489
|
-
"id": "sign",
|
|
1490
|
-
"appDir": "TopoloSign",
|
|
1491
|
-
"name": "topolo-sign",
|
|
1492
|
-
"packageName": "topolo-sign",
|
|
1493
|
-
"productionUrl": "https://sign.topolo.app",
|
|
1494
|
-
"services": [
|
|
1495
|
-
"sign"
|
|
1496
|
-
],
|
|
1497
|
-
"deployTargets": [
|
|
1498
|
-
{
|
|
1499
|
-
"name": "topolo-sign",
|
|
1500
|
-
"kind": "worker",
|
|
1501
|
-
"apiUrl": "https://sign.topolo.app",
|
|
1502
|
-
"domains": [
|
|
1503
|
-
"sign.topolo.app"
|
|
1504
|
-
],
|
|
1505
|
-
"routes": [
|
|
1506
|
-
"sign.topolo.app"
|
|
1507
|
-
],
|
|
1508
|
-
"appId": "sign"
|
|
1509
|
-
},
|
|
1510
|
-
{
|
|
1511
|
-
"name": "topolo-sign-api",
|
|
1512
|
-
"kind": "worker",
|
|
1513
|
-
"apiUrl": null,
|
|
1514
|
-
"domains": [],
|
|
1515
|
-
"routes": [],
|
|
1516
|
-
"appId": null
|
|
1517
|
-
}
|
|
1518
|
-
]
|
|
1519
|
-
},
|
|
1520
|
-
"social_studio": {
|
|
1521
|
-
"id": "social_studio",
|
|
1522
|
-
"appDir": "TopoloSocialStudio",
|
|
1523
|
-
"name": "topolo-social-studio",
|
|
1524
|
-
"packageName": "topolo-social-studio",
|
|
1525
|
-
"productionUrl": "https://studio.topolo.app",
|
|
1526
|
-
"services": [
|
|
1527
|
-
"social_studio"
|
|
1528
|
-
],
|
|
1529
|
-
"deployTargets": [
|
|
1530
|
-
{
|
|
1531
|
-
"name": "topolo-social-studio",
|
|
1532
|
-
"kind": "worker",
|
|
1533
|
-
"apiUrl": "https://studio.topolo.app",
|
|
1534
|
-
"domains": [
|
|
1535
|
-
"studio.topolo.app"
|
|
1536
|
-
],
|
|
1537
|
-
"routes": [
|
|
1538
|
-
"studio.topolo.app"
|
|
1539
|
-
],
|
|
1540
|
-
"appId": "social_studio"
|
|
1541
|
-
},
|
|
1542
|
-
{
|
|
1543
|
-
"name": "topolo-social-studio-api",
|
|
1544
|
-
"kind": "worker",
|
|
1545
|
-
"apiUrl": null,
|
|
1546
|
-
"domains": [],
|
|
1547
|
-
"routes": [],
|
|
1548
|
-
"appId": null
|
|
1549
|
-
},
|
|
1550
|
-
{
|
|
1551
|
-
"name": "topolo-social-studio-queue",
|
|
1552
|
-
"kind": "worker",
|
|
1553
|
-
"apiUrl": null,
|
|
1554
|
-
"domains": [],
|
|
1555
|
-
"routes": [],
|
|
1556
|
-
"appId": null
|
|
1557
|
-
}
|
|
1558
|
-
]
|
|
1559
|
-
},
|
|
1560
|
-
"socialize": {
|
|
1561
|
-
"id": "socialize",
|
|
1562
|
-
"appDir": "TopoloSocialize",
|
|
1563
|
-
"name": "socialize",
|
|
1564
|
-
"packageName": "topolo-socialize",
|
|
1565
|
-
"productionUrl": "https://socialize.topolo.app",
|
|
1566
|
-
"services": [
|
|
1567
|
-
"socialize"
|
|
1568
|
-
],
|
|
1569
|
-
"deployTargets": [
|
|
1570
|
-
{
|
|
1571
|
-
"name": "topolo-socialize",
|
|
1572
|
-
"kind": "worker",
|
|
1573
|
-
"apiUrl": "https://socialize.topolo.app",
|
|
1574
|
-
"domains": [
|
|
1575
|
-
"socialize.topolo.app"
|
|
1576
|
-
],
|
|
1577
|
-
"routes": [
|
|
1578
|
-
"socialize.topolo.app"
|
|
1579
|
-
],
|
|
1580
|
-
"appId": "socialize"
|
|
1581
|
-
},
|
|
1582
|
-
{
|
|
1583
|
-
"name": "topolo-socialize-api",
|
|
1584
|
-
"kind": "worker",
|
|
1585
|
-
"apiUrl": "https://socialize-api.topolo.app",
|
|
1586
|
-
"domains": [
|
|
1587
|
-
"socialize-api.topolo.app"
|
|
1588
|
-
],
|
|
1589
|
-
"routes": [
|
|
1590
|
-
"socialize-api.topolo.app"
|
|
1591
|
-
],
|
|
1592
|
-
"appId": null
|
|
1593
|
-
},
|
|
1594
|
-
{
|
|
1595
|
-
"name": "topolo-socialize-scheduler",
|
|
1596
|
-
"kind": "worker",
|
|
1597
|
-
"apiUrl": null,
|
|
1598
|
-
"domains": [],
|
|
1599
|
-
"routes": [],
|
|
1600
|
-
"appId": null
|
|
1601
|
-
}
|
|
1602
|
-
]
|
|
1603
|
-
},
|
|
1604
|
-
"spaces": {
|
|
1605
|
-
"id": "spaces",
|
|
1606
|
-
"appDir": "TopoloSpaces",
|
|
1607
|
-
"name": "topolo-spaces",
|
|
1608
|
-
"packageName": "@topolo/spaces",
|
|
1609
|
-
"productionUrl": "https://spaces.topolo.app",
|
|
1610
|
-
"services": [
|
|
1611
|
-
"spaces"
|
|
1612
|
-
],
|
|
1613
|
-
"deployTargets": [
|
|
1614
|
-
{
|
|
1615
|
-
"name": "topolo-spaces",
|
|
1616
|
-
"kind": "worker",
|
|
1617
|
-
"apiUrl": "https://spaces.topolo.app",
|
|
1618
|
-
"domains": [
|
|
1619
|
-
"spaces.topolo.app"
|
|
1620
|
-
],
|
|
1621
|
-
"routes": [
|
|
1622
|
-
"spaces.topolo.app"
|
|
1623
|
-
],
|
|
1624
|
-
"appId": "spaces"
|
|
1625
|
-
},
|
|
1626
|
-
{
|
|
1627
|
-
"name": "topolo-spaces-api",
|
|
1628
|
-
"kind": "worker",
|
|
1629
|
-
"apiUrl": null,
|
|
1630
|
-
"domains": [],
|
|
1631
|
-
"routes": [],
|
|
1632
|
-
"appId": null
|
|
1633
|
-
}
|
|
1634
|
-
]
|
|
1635
|
-
},
|
|
1636
|
-
"status": {
|
|
1637
|
-
"id": "status",
|
|
1638
|
-
"appDir": "TopoloStatus",
|
|
1639
|
-
"name": "topolo-status",
|
|
1640
|
-
"packageName": "topolo-status",
|
|
1641
|
-
"productionUrl": "https://status.topolo.app",
|
|
1642
|
-
"services": [
|
|
1643
|
-
"status"
|
|
1644
|
-
],
|
|
1645
|
-
"deployTargets": [
|
|
1646
|
-
{
|
|
1647
|
-
"name": "topolo-status",
|
|
1648
|
-
"kind": "worker",
|
|
1649
|
-
"apiUrl": "https://status.topolo.app",
|
|
1650
|
-
"domains": [
|
|
1651
|
-
"status.topolo.app"
|
|
1652
|
-
],
|
|
1653
|
-
"routes": [
|
|
1654
|
-
"status.topolo.app"
|
|
1655
|
-
],
|
|
1656
|
-
"appId": "status"
|
|
1657
|
-
}
|
|
1658
|
-
]
|
|
1659
|
-
},
|
|
1660
|
-
"success": {
|
|
1661
|
-
"id": "success",
|
|
1662
|
-
"appDir": "TopoloSuccess",
|
|
1663
|
-
"name": "topolo-success",
|
|
1664
|
-
"packageName": "topolo-success",
|
|
1665
|
-
"productionUrl": "https://success.topolo.app",
|
|
1666
|
-
"services": [
|
|
1667
|
-
"success"
|
|
1668
|
-
],
|
|
1669
|
-
"deployTargets": [
|
|
1670
|
-
{
|
|
1671
|
-
"name": "topolo-success",
|
|
1672
|
-
"kind": "worker",
|
|
1673
|
-
"apiUrl": "https://success.topolo.app",
|
|
1674
|
-
"domains": [
|
|
1675
|
-
"success.topolo.app"
|
|
1676
|
-
],
|
|
1677
|
-
"routes": [
|
|
1678
|
-
"success.topolo.app"
|
|
1679
|
-
],
|
|
1680
|
-
"appId": "success"
|
|
1681
|
-
},
|
|
1682
|
-
{
|
|
1683
|
-
"name": "topolo-success-api",
|
|
1684
|
-
"kind": "worker",
|
|
1685
|
-
"apiUrl": null,
|
|
1686
|
-
"domains": [],
|
|
1687
|
-
"routes": [],
|
|
1688
|
-
"appId": null
|
|
1689
|
-
}
|
|
1690
|
-
]
|
|
1691
|
-
},
|
|
1692
|
-
"support": {
|
|
1693
|
-
"id": "support",
|
|
1694
|
-
"appDir": "TopoloSupport",
|
|
1695
|
-
"name": "topolo-support",
|
|
1696
|
-
"packageName": "topolo-support",
|
|
1697
|
-
"productionUrl": "https://support.topolo.app",
|
|
1698
|
-
"services": [
|
|
1699
|
-
"support"
|
|
1700
|
-
],
|
|
1701
|
-
"deployTargets": [
|
|
1702
|
-
{
|
|
1703
|
-
"name": "topolo-support",
|
|
1704
|
-
"kind": "worker",
|
|
1705
|
-
"apiUrl": "https://support.topolo.app",
|
|
1706
|
-
"domains": [
|
|
1707
|
-
"support.topolo.app"
|
|
1708
|
-
],
|
|
1709
|
-
"routes": [
|
|
1710
|
-
"support.topolo.app/*"
|
|
1711
|
-
],
|
|
1712
|
-
"appId": "support"
|
|
1713
|
-
},
|
|
1714
|
-
{
|
|
1715
|
-
"name": "topolo-support-api",
|
|
1716
|
-
"kind": "worker",
|
|
1717
|
-
"apiUrl": null,
|
|
1718
|
-
"domains": [],
|
|
1719
|
-
"routes": [],
|
|
1720
|
-
"appId": null
|
|
1721
|
-
}
|
|
1722
|
-
]
|
|
1723
|
-
},
|
|
1724
|
-
"survey": {
|
|
1725
|
-
"id": "survey",
|
|
1726
|
-
"appDir": "TopoloSurvey",
|
|
1727
|
-
"name": "topolo-survey",
|
|
1728
|
-
"packageName": "topolo-survey",
|
|
1729
|
-
"productionUrl": "https://survey.topolo.app",
|
|
1730
|
-
"services": [
|
|
1731
|
-
"survey"
|
|
1732
|
-
],
|
|
1733
|
-
"deployTargets": [
|
|
1734
|
-
{
|
|
1735
|
-
"name": "topolo-survey",
|
|
1736
|
-
"kind": "worker",
|
|
1737
|
-
"apiUrl": "https://survey.topolo.app",
|
|
1738
|
-
"domains": [
|
|
1739
|
-
"survey.topolo.app"
|
|
1740
|
-
],
|
|
1741
|
-
"routes": [
|
|
1742
|
-
"survey.topolo.app"
|
|
1743
|
-
],
|
|
1744
|
-
"appId": "survey"
|
|
1745
|
-
},
|
|
1746
|
-
{
|
|
1747
|
-
"name": "topolo-survey-api",
|
|
1748
|
-
"kind": "worker",
|
|
1749
|
-
"apiUrl": null,
|
|
1750
|
-
"domains": [],
|
|
1751
|
-
"routes": [],
|
|
1752
|
-
"appId": null
|
|
1753
|
-
}
|
|
1754
|
-
]
|
|
1755
|
-
},
|
|
1756
|
-
"voice": {
|
|
1757
|
-
"id": "voice",
|
|
1758
|
-
"appDir": "TopoloVoice",
|
|
1759
|
-
"name": "topolo-voice",
|
|
1760
|
-
"packageName": "topolo-voice",
|
|
1761
|
-
"productionUrl": "https://voice.topolo.app",
|
|
1762
|
-
"services": [
|
|
1763
|
-
"voice"
|
|
1764
|
-
],
|
|
1765
|
-
"deployTargets": [
|
|
1766
|
-
{
|
|
1767
|
-
"name": "topolo-voice",
|
|
1768
|
-
"kind": "worker",
|
|
1769
|
-
"apiUrl": "https://voice.topolo.app",
|
|
1770
|
-
"domains": [
|
|
1771
|
-
"voice.topolo.app"
|
|
1772
|
-
],
|
|
1773
|
-
"routes": [
|
|
1774
|
-
"voice.topolo.app"
|
|
1775
|
-
],
|
|
1776
|
-
"appId": "voice"
|
|
1777
|
-
},
|
|
1778
|
-
{
|
|
1779
|
-
"name": "topolo-voice-api",
|
|
1780
|
-
"kind": "worker",
|
|
1781
|
-
"apiUrl": null,
|
|
1782
|
-
"domains": [],
|
|
1783
|
-
"routes": [],
|
|
1784
|
-
"appId": null
|
|
1785
|
-
}
|
|
1786
|
-
]
|
|
1787
|
-
},
|
|
1788
|
-
"web": {
|
|
1789
|
-
"id": "web",
|
|
1790
|
-
"appDir": "TopoloWeb",
|
|
1791
|
-
"name": "topoloweb",
|
|
1792
|
-
"packageName": "topolo-web",
|
|
1793
|
-
"productionUrl": "https://web.topolo.app",
|
|
1794
|
-
"services": [
|
|
1795
|
-
"web"
|
|
1796
|
-
],
|
|
1797
|
-
"deployTargets": [
|
|
1798
|
-
{
|
|
1799
|
-
"name": "topolo-web-api",
|
|
1800
|
-
"kind": "worker",
|
|
1801
|
-
"apiUrl": null,
|
|
1802
|
-
"domains": [],
|
|
1803
|
-
"routes": [],
|
|
1804
|
-
"appId": null
|
|
1805
|
-
},
|
|
1806
|
-
{
|
|
1807
|
-
"name": "topolo-web-runtime",
|
|
1808
|
-
"kind": "worker",
|
|
1809
|
-
"apiUrl": "https://sites.topolo.app",
|
|
1810
|
-
"domains": [
|
|
1811
|
-
"sites.topolo.app"
|
|
1812
|
-
],
|
|
1813
|
-
"routes": [
|
|
1814
|
-
"*.sites.topolo.app/*",
|
|
1815
|
-
"sites.topolo.app"
|
|
1816
|
-
],
|
|
1817
|
-
"appId": null
|
|
1818
|
-
},
|
|
1819
|
-
{
|
|
1820
|
-
"name": "topolo-web-studio",
|
|
1821
|
-
"kind": "worker",
|
|
1822
|
-
"apiUrl": "https://web.topolo.app",
|
|
1823
|
-
"domains": [
|
|
1824
|
-
"web.topolo.app"
|
|
1825
|
-
],
|
|
1826
|
-
"routes": [
|
|
1827
|
-
"web.topolo.app"
|
|
1828
|
-
],
|
|
1829
|
-
"appId": "web"
|
|
1830
|
-
}
|
|
1831
|
-
]
|
|
1832
|
-
}
|
|
1833
|
-
};
|
|
1834
|
-
function friendlyAppId(entry) {
|
|
1835
|
-
for (const value of [entry.slug, entry.name, entry.appId]) {
|
|
1836
|
-
if (typeof value !== "string" || !value.trim()) continue;
|
|
1837
|
-
const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
|
|
1838
|
-
const withoutPrefix = normalized.replace(/^(?:app[_-])?topolo[-_]+/, "");
|
|
1839
|
-
if (withoutPrefix) return withoutPrefix;
|
|
1840
|
-
}
|
|
1841
|
-
return entry.appId;
|
|
1842
|
-
}
|
|
1843
|
-
function idVariants(value) {
|
|
1844
|
-
const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
|
|
1845
|
-
return [.../* @__PURE__ */ new Set([
|
|
1846
|
-
normalized,
|
|
1847
|
-
normalized.replace(/-/g, "_"),
|
|
1848
|
-
normalized.replace(/_/g, "-")
|
|
1849
|
-
])].filter(Boolean);
|
|
1850
|
-
}
|
|
1851
|
-
var STATIC_APPLICATIONS = APPLICATIONS;
|
|
1852
|
-
function staticApplicationForId(id) {
|
|
1853
|
-
for (const variant of idVariants(id)) {
|
|
1854
|
-
const entry = STATIC_APPLICATIONS[variant];
|
|
1855
|
-
if (entry) return entry;
|
|
1856
|
-
}
|
|
1857
|
-
return null;
|
|
1858
|
-
}
|
|
1859
|
-
function applicationEntryFromCatalog(live) {
|
|
1860
|
-
const id = friendlyAppId(live);
|
|
1861
|
-
return {
|
|
1862
|
-
id,
|
|
1863
|
-
appDir: "-",
|
|
1864
|
-
name: live.name ?? live.slug ?? live.appId,
|
|
1865
|
-
packageName: null,
|
|
1866
|
-
productionUrl: live.launchUrl ?? live.apiBaseUrl ?? null,
|
|
1867
|
-
services: live.apiBaseUrl ? [id] : [],
|
|
1868
|
-
deployTargets: []
|
|
1869
|
-
};
|
|
1870
|
-
}
|
|
1871
|
-
var DIRECTORY_PAGE_LIMIT = 100;
|
|
1872
|
-
var DIRECTORY_MAX_PAGES = 100;
|
|
1873
|
-
function staticApplicationDirectory() {
|
|
1874
|
-
const applications = Object.entries(STATIC_APPLICATIONS).map(([id, application]) => ({
|
|
1875
|
-
id,
|
|
1876
|
-
application,
|
|
1877
|
-
source: "static",
|
|
1878
|
-
live: null
|
|
1879
|
-
})).sort((left, right) => left.id.localeCompare(right.id));
|
|
1880
|
-
return { source: "static_fallback", applications };
|
|
1881
|
-
}
|
|
1882
|
-
function directoryApplicationFromLive(live) {
|
|
1883
|
-
const id = friendlyAppId(live);
|
|
1884
|
-
return {
|
|
1885
|
-
id,
|
|
1886
|
-
// Prefer the static snapshot's richer repo/deploy metadata when the live
|
|
1887
|
-
// registry confirms the app; synthesize from the live entry otherwise.
|
|
1888
|
-
application: staticApplicationForId(id) ?? applicationEntryFromCatalog(live),
|
|
1889
|
-
source: "live",
|
|
1890
|
-
live
|
|
1891
|
-
};
|
|
1892
|
-
}
|
|
1893
|
-
async function listApplicationDirectory(client) {
|
|
1894
|
-
if (!client) return staticApplicationDirectory();
|
|
1895
|
-
let services;
|
|
1896
|
-
try {
|
|
1897
|
-
services = [];
|
|
1898
|
-
let cursor;
|
|
1899
|
-
for (let page = 0; page < DIRECTORY_MAX_PAGES; page += 1) {
|
|
1900
|
-
const result = await client.searchServices({
|
|
1901
|
-
...cursor ? { cursor } : {},
|
|
1902
|
-
limit: DIRECTORY_PAGE_LIMIT
|
|
1903
|
-
});
|
|
1904
|
-
services.push(...result.services);
|
|
1905
|
-
if (!result.page.nextCursor) break;
|
|
1906
|
-
cursor = result.page.nextCursor;
|
|
1907
|
-
}
|
|
1908
|
-
} catch {
|
|
1909
|
-
return staticApplicationDirectory();
|
|
1910
|
-
}
|
|
1911
|
-
const seen = /* @__PURE__ */ new Map();
|
|
1912
|
-
for (const live of services) {
|
|
1913
|
-
const entry = directoryApplicationFromLive(live);
|
|
1914
|
-
if (!seen.has(entry.id)) seen.set(entry.id, entry);
|
|
1915
|
-
}
|
|
1916
|
-
return {
|
|
1917
|
-
source: "live",
|
|
1918
|
-
applications: [...seen.values()].sort((left, right) => left.id.localeCompare(right.id))
|
|
1919
|
-
};
|
|
1920
|
-
}
|
|
1921
|
-
function matchesDirectoryApplication(app, needle) {
|
|
1922
|
-
const candidates = new Set(idVariants(needle));
|
|
1923
|
-
const keys = [
|
|
1924
|
-
app.id,
|
|
1925
|
-
app.application.id,
|
|
1926
|
-
app.application.name,
|
|
1927
|
-
app.live?.appId,
|
|
1928
|
-
app.live?.slug,
|
|
1929
|
-
app.live?.name
|
|
1930
|
-
];
|
|
1931
|
-
for (const key of keys) {
|
|
1932
|
-
if (typeof key !== "string" || !key.trim()) continue;
|
|
1933
|
-
for (const variant of idVariants(key)) {
|
|
1934
|
-
if (candidates.has(variant)) return true;
|
|
1935
|
-
}
|
|
1936
|
-
for (const variant of idVariants(friendlyAppId({ appId: key }))) {
|
|
1937
|
-
if (candidates.has(variant)) return true;
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
return false;
|
|
1941
|
-
}
|
|
1942
|
-
function formatDirectoryApplicationIds(directory) {
|
|
1943
|
-
const ids = directory.applications.map((app) => app.id).sort();
|
|
1944
|
-
const preview = ids.slice(0, 50).join(", ");
|
|
1945
|
-
const suffix = ids.length > 50 ? `, ... +${ids.length - 50} more` : "";
|
|
1946
|
-
const label = directory.source === "live" ? "Applications available to this credential" : "Applications in the bundled static snapshot (live registry unreachable)";
|
|
1947
|
-
return `${label}: ${preview}${suffix}`;
|
|
1948
|
-
}
|
|
1949
|
-
async function resolveApplicationDirectoryEntry(client, application) {
|
|
1950
|
-
const needle = typeof application === "string" ? application.trim() : "";
|
|
1951
|
-
if (!needle) {
|
|
1952
|
-
throw new TopoloSdkError("unknown_application", "application is required");
|
|
1953
|
-
}
|
|
1954
|
-
if (client) {
|
|
1955
|
-
let live = null;
|
|
1956
|
-
try {
|
|
1957
|
-
live = await client.getService(needle);
|
|
1958
|
-
} catch {
|
|
1959
|
-
live = null;
|
|
1960
|
-
}
|
|
1961
|
-
if (live) return directoryApplicationFromLive(live);
|
|
1962
|
-
}
|
|
1963
|
-
const directory = await listApplicationDirectory(client);
|
|
1964
|
-
const match = directory.applications.find((app) => matchesDirectoryApplication(app, needle));
|
|
1965
|
-
if (match) return match;
|
|
1966
|
-
throw new TopoloSdkError(
|
|
1967
|
-
"unknown_application",
|
|
1968
|
-
`Unknown application "${application}". ${formatDirectoryApplicationIds(directory)}`
|
|
1969
|
-
);
|
|
1970
|
-
}
|
|
1971
|
-
var STATIC_APPLICATIONS2 = APPLICATIONS;
|
|
1972
|
-
var APPLICATION_REQUIREMENTS_VERSION = "2026-08-11.1";
|
|
1973
|
-
var APPLICATION_REQUIREMENTS = [
|
|
1974
|
-
{
|
|
1975
|
-
id: "platform-metadata",
|
|
1976
|
-
title: "Register the application in platform metadata",
|
|
1977
|
-
summary: "Every app must have a stable application ID, CloudControl metadata for deployed surfaces, and package metadata where code is shipped.",
|
|
1978
|
-
appliesTo: ["all"],
|
|
1979
|
-
evidence: [
|
|
1980
|
-
"topolo.cloudcontrol.json exists for every deployed app surface",
|
|
1981
|
-
"package.json names the package or app when the repo ships code",
|
|
1982
|
-
"The live platform registry exposes the app through topolo apps and MCP discovery"
|
|
1983
|
-
],
|
|
1984
|
-
implementation: [
|
|
1985
|
-
"Register the app in the live platform registry (Auth service catalog) before exposing it to agents",
|
|
1986
|
-
"Keep production URLs, Worker names, Pages projects, routes, domains, and bindings current in CloudControl metadata",
|
|
1987
|
-
"Do not leave a new Topolo* app directory without an explicit application ID"
|
|
1988
|
-
]
|
|
1989
|
-
},
|
|
1990
|
-
{
|
|
1991
|
-
id: "canonical-docs",
|
|
1992
|
-
title: "Update canonical docs with the code change",
|
|
1993
|
-
summary: "TopoloDocs is the source of truth for application behavior, ownership, deployment shape, auth boundaries, and operations.",
|
|
1994
|
-
appliesTo: ["all"],
|
|
1995
|
-
evidence: [
|
|
1996
|
-
"Matching system registry entry exists in TopoloDocs",
|
|
1997
|
-
"Internal handbook coverage exists for the system",
|
|
1998
|
-
"npm run validate passes in TopoloDocs when docs are touched"
|
|
1999
|
-
],
|
|
2000
|
-
implementation: [
|
|
2001
|
-
"Read the matching TopoloDocs system entry before coding",
|
|
2002
|
-
"Update docs in the same change when behavior, API, auth, data ownership, deployment, or operations change",
|
|
2003
|
-
"Update last_verified dates on touched canonical pages"
|
|
2004
|
-
]
|
|
2005
|
-
},
|
|
2006
|
-
{
|
|
2007
|
-
id: "shared-auth-boundary",
|
|
2008
|
-
title: "Use the shared Topolo auth boundary",
|
|
2009
|
-
summary: "Applications must derive identity, organization, scopes, and app entitlement from TopoloAuth rather than inventing parallel auth state.",
|
|
2010
|
-
appliesTo: ["browser", "api", "tooling", "agent_surface"],
|
|
2011
|
-
evidence: [
|
|
2012
|
-
"@topolo/auth-client or @topolo-io/worker-runtime is used where applicable",
|
|
2013
|
-
"No public command, tool, or SDK method accepts orgId",
|
|
2014
|
-
"App slug and Auth app registration are documented for protected APIs"
|
|
2015
|
-
],
|
|
2016
|
-
implementation: [
|
|
2017
|
-
"Use shared auth/runtime packages instead of copying token parsing, gateway, or browser storage flows",
|
|
2018
|
-
"Derive organization from the credential on every request",
|
|
2019
|
-
"Route OAuth, refresh, logout, and SSO paths according to the shared Auth contract"
|
|
2020
|
-
]
|
|
2021
|
-
},
|
|
2022
|
-
{
|
|
2023
|
-
id: "shared-shell-and-launcher",
|
|
2024
|
-
title: "Use the shared shell, launcher, and account menu",
|
|
2025
|
-
summary: "Browser applications should feel like one platform and should not create isolated navigation or account surfaces.",
|
|
2026
|
-
appliesTo: ["browser"],
|
|
2027
|
-
evidence: [
|
|
2028
|
-
"@topolo-io/app-shell is used for the platform shell where the app has authenticated UI",
|
|
2029
|
-
"@topolo-io/app-launcher keeps the shared launcher/app switcher reachable from authenticated app chrome",
|
|
2030
|
-
"Account menu and sign-out behavior use TopoloAppShell or the ui-kit TopoloAccountMenu primitive",
|
|
2031
|
-
"Repeated app body surfaces use shared TopoloAppLayout primitives instead of local stat cards, empty states, or status badges",
|
|
2032
|
-
"Local preview supports deterministic visual QA fixtures for overview, list, detail, settings, and empty/loading states",
|
|
2033
|
-
"Authenticated app body content stays inside the viewport on mobile and desktop widths"
|
|
2034
|
-
],
|
|
2035
|
-
implementation: [
|
|
2036
|
-
"Use @topolo-io/app-shell TopoloAppShell for authenticated app layouts",
|
|
2037
|
-
"Use the @topolo-io/ui-kit TopoloAccountMenu primitive when a partial-shell surface still needs account chrome",
|
|
2038
|
-
"Pass app-specific account actions as additive menu items instead of forking dropdowns",
|
|
2039
|
-
"Use shared design tokens and platform UI primitives before creating local equivalents",
|
|
2040
|
-
"Use TopoloAppMetricCard, TopoloAppEmptyState, TopoloAppStatusBadge, TopoloAppGrid, and TopoloAppSection for repeated app body patterns",
|
|
2041
|
-
"Keep visual QA fixtures available through visualQa=1 or __devpreview=1 so screenshot checks do not depend on live API state",
|
|
2042
|
-
"Keep mobile and desktop shell behavior aligned with the platform UI kit",
|
|
2043
|
-
"Constrain the app workspace-view layout wrappers with min-width: 0, width: auto, and viewport-aware max-width so shell padding cannot create page-level horizontal overflow"
|
|
2044
|
-
]
|
|
2045
|
-
},
|
|
2046
|
-
{
|
|
2047
|
-
id: "app-registration-and-scopes",
|
|
2048
|
-
title: "Register protected APIs as platform apps",
|
|
2049
|
-
summary: "Callable app APIs need a stable app ID, app slug, permissions, role bundles, and API-key scopes before agents or users rely on them.",
|
|
2050
|
-
appliesTo: ["api"],
|
|
2051
|
-
evidence: [
|
|
2052
|
-
"Auth app catalog contains the app ID and slug",
|
|
2053
|
-
"Permissions, role bundles, and API-key scopes exist for the app",
|
|
2054
|
-
"SDK, CLI, and MCP app discovery include the callable production API",
|
|
2055
|
-
"CloudControl production HTTP targets classify agent reachability with sdk_app_id or agent_callable:false"
|
|
2056
|
-
],
|
|
2057
|
-
implementation: [
|
|
2058
|
-
"Add production Worker api_url metadata and sdk_app_id for every agent-callable API target",
|
|
2059
|
-
"Mark browser, documentation, and runtime-only Worker targets with agent_callable:false",
|
|
2060
|
-
"Expose stable app IDs through live catalog discovery",
|
|
2061
|
-
"Keep typed SDK/CLI/MCP surfaces in sync when an API contract stabilizes"
|
|
2062
|
-
]
|
|
2063
|
-
},
|
|
2064
|
-
{
|
|
2065
|
-
id: "organization-scoped-data",
|
|
2066
|
-
title: "Keep application data organization-scoped",
|
|
2067
|
-
summary: "Protected data access must be scoped by the authenticated organization at the backend boundary, not by client-provided hints.",
|
|
2068
|
-
appliesTo: ["api"],
|
|
2069
|
-
evidence: [
|
|
2070
|
-
"Backend request handlers derive org membership from credential validation",
|
|
2071
|
-
"Queries and Durable Object keys include the authenticated organization boundary where data is tenant-owned",
|
|
2072
|
-
"MCP and CLI inputs do not include orgId overrides"
|
|
2073
|
-
],
|
|
2074
|
-
implementation: [
|
|
2075
|
-
"Reject or ignore organization hints supplied by clients",
|
|
2076
|
-
"Bind SQL, KV, R2, queue, and Durable Object access to the authenticated organization",
|
|
2077
|
-
"Document service-local exceptions explicitly in the system security assurance record"
|
|
2078
|
-
]
|
|
2079
|
-
},
|
|
2080
|
-
{
|
|
2081
|
-
id: "cloudflare-deployment-contract",
|
|
2082
|
-
title: "Keep Cloudflare deployment metadata complete",
|
|
2083
|
-
summary: "Workers, Pages, bindings, routes, env vars, queues, cron triggers, and deployment commands must be discoverable through CloudControl metadata.",
|
|
2084
|
-
appliesTo: ["browser", "api"],
|
|
2085
|
-
evidence: [
|
|
2086
|
-
"topolo.cloudcontrol.json lists production deploy targets and URLs",
|
|
2087
|
-
"Wrangler/Pages configuration matches CloudControl metadata",
|
|
2088
|
-
"Secrets and environment variables are not committed"
|
|
2089
|
-
],
|
|
2090
|
-
implementation: [
|
|
2091
|
-
"Update CloudControl metadata whenever deployment topology changes",
|
|
2092
|
-
"Prefer Cloudflare-native runtime surfaces for new Topolo app infrastructure",
|
|
2093
|
-
"Keep Worker bindings and Pages domains explicit enough for agents to inspect before deploying"
|
|
2094
|
-
]
|
|
2095
|
-
},
|
|
2096
|
-
{
|
|
2097
|
-
id: "agent-and-operator-surfaces",
|
|
2098
|
-
title: "Expose agent-safe operations through SDK, CLI, and MCP",
|
|
2099
|
-
summary: "Agent-accessible functionality should be discoverable, scope-gated, and write-confirmed instead of hidden behind ad hoc HTTP calls.",
|
|
2100
|
-
appliesTo: ["api", "tooling", "agent_surface"],
|
|
2101
|
-
evidence: [
|
|
2102
|
-
"Read operations have typed SDK/CLI/MCP surfaces once their contract stabilizes",
|
|
2103
|
-
"Mutating generic calls require --confirm or confirm:true",
|
|
2104
|
-
"MCP tools never accept orgId and advertise only appropriate scope-gated tools"
|
|
2105
|
-
],
|
|
2106
|
-
implementation: [
|
|
2107
|
-
"Add SDK types first, then CLI commands and MCP tools for durable workflows",
|
|
2108
|
-
"Use generic API passthrough only as a temporary escape hatch",
|
|
2109
|
-
"Keep command output JSON-stable for agents and human-readable with --no-json"
|
|
2110
|
-
]
|
|
2111
|
-
},
|
|
2112
|
-
{
|
|
2113
|
-
id: "native-dashboard-widget",
|
|
2114
|
-
title: "Expose a native dashboard widget contract",
|
|
2115
|
-
summary: "Launchable first-party applications must populate TopoloOne live workspace through an app-owned /api/widget endpoint using the SDK widget response contract.",
|
|
2116
|
-
appliesTo: ["api"],
|
|
2117
|
-
evidence: [
|
|
2118
|
-
"The app-owned API exposes GET /api/widget",
|
|
2119
|
-
"The endpoint returns TopoloWidgetApiResponse from @topolo/sdk",
|
|
2120
|
-
"Every returned widget includes snapshot.summary with app-owned operational prose",
|
|
2121
|
-
"The endpoint derives organization/user context from the app auth boundary"
|
|
2122
|
-
],
|
|
2123
|
-
implementation: [
|
|
2124
|
-
"Import createTopoloWidgetResponse from @topolo/sdk in the app backend",
|
|
2125
|
-
"Return native app metrics or action widgets instead of generic launch-card fallbacks",
|
|
2126
|
-
"Populate snapshot.summary in the producing app for every widget instead of synthesizing summaries in TopoloOne",
|
|
2127
|
-
"Add a route-level test that validates the response with validateTopoloWidgetResponse"
|
|
2128
|
-
]
|
|
2129
|
-
},
|
|
2130
|
-
{
|
|
2131
|
-
id: "observability-and-audit",
|
|
2132
|
-
title: "Emit platform audit and debugging signals",
|
|
2133
|
-
summary: "Requests, agent actions, and operational workflows need traceable IDs, client labels, and useful failure evidence.",
|
|
2134
|
-
appliesTo: ["api", "tooling", "agent_surface"],
|
|
2135
|
-
evidence: [
|
|
2136
|
-
"Requests carry X-Topolo-Client and X-Topolo-Request-Id where the SDK is involved",
|
|
2137
|
-
"Agent-originated calls can include X-Topolo-Agent",
|
|
2138
|
-
"Failure modes are documented with request IDs or deployment/log lookup paths"
|
|
2139
|
-
],
|
|
2140
|
-
implementation: [
|
|
2141
|
-
"Use TopoloClient for platform API calls rather than raw fetch where practical",
|
|
2142
|
-
"Keep logs free of secrets and tokens",
|
|
2143
|
-
"Document live smoke commands and request-id correlation in canonical docs"
|
|
2144
|
-
]
|
|
2145
|
-
},
|
|
2146
|
-
{
|
|
2147
|
-
id: "verification-gates",
|
|
2148
|
-
title: "Ship with build, test, docs, and smoke verification",
|
|
2149
|
-
summary: "A Topolo app change is not complete until the relevant code checks and docs checks have been run or explicitly reported as blocked.",
|
|
2150
|
-
appliesTo: ["all"],
|
|
2151
|
-
evidence: [
|
|
2152
|
-
"Relevant package build/typecheck/test commands pass",
|
|
2153
|
-
"TopoloDocs validation passes when docs are touched",
|
|
2154
|
-
"Live or local smoke evidence is captured for user-visible or API behavior"
|
|
2155
|
-
],
|
|
2156
|
-
implementation: [
|
|
2157
|
-
"Prefer existing package scripts before adding new tooling",
|
|
2158
|
-
"Run app-specific checks and shared docs validation for behavior changes",
|
|
2159
|
-
"Report any skipped verification with the blocker and residual risk"
|
|
2160
|
-
]
|
|
2161
|
-
}
|
|
2162
|
-
];
|
|
2163
|
-
function applicationRequirementScopes(application) {
|
|
2164
|
-
const scopes = /* @__PURE__ */ new Set(["all"]);
|
|
2165
|
-
const appId = application.id;
|
|
2166
|
-
const hasBrowserSurface = Boolean(application.productionUrl) || // String() keeps this guard valid after the Pages->Worker migration narrowed
|
|
2167
|
-
// the generated kind union to "worker" (no "pages" literal remains).
|
|
2168
|
-
application.deployTargets.some((target) => String(target.kind) === "pages");
|
|
2169
|
-
const hasApiSurface = application.services.length > 0 || application.deployTargets.some((target) => Boolean(target.appId));
|
|
2170
|
-
if (hasBrowserSurface) scopes.add("browser");
|
|
2171
|
-
if (hasApiSurface) scopes.add("api");
|
|
2172
|
-
if (appId === "cli" || appId === "mcp" || appId === "cloudcontrol") {
|
|
2173
|
-
scopes.add("tooling");
|
|
2174
|
-
}
|
|
2175
|
-
if (appId === "cli" || appId === "mcp" || hasApiSurface) {
|
|
2176
|
-
scopes.add("agent_surface");
|
|
2177
|
-
}
|
|
2178
|
-
return Array.from(scopes);
|
|
2179
|
-
}
|
|
2180
|
-
function fallbackApplication(id) {
|
|
2181
|
-
const entry = STATIC_APPLICATIONS2[id];
|
|
2182
|
-
if (!entry) {
|
|
2183
|
-
throw new Error(
|
|
2184
|
-
`Unknown application "${id}" in the static fallback snapshot. Resolve applications from the live registry (resolveApplicationDirectoryEntry) and pass the entry directly.`
|
|
302
|
+
this.path = path;
|
|
303
|
+
this.requestId = canonicalTopoloRequestId(options.requestId);
|
|
304
|
+
this.retryAfter = canonicalTopoloRetryAfter(options.retryAfter);
|
|
305
|
+
this.details = extractTopoloServiceErrorDetails(
|
|
306
|
+
options.details === void 0 ? body : { details: options.details }
|
|
2185
307
|
);
|
|
2186
308
|
}
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
const application = typeof applicationOrId === "string" ? fallbackApplication(applicationOrId) : applicationOrId;
|
|
2191
|
-
const scopes = new Set(applicationRequirementScopes(application));
|
|
2192
|
-
return APPLICATION_REQUIREMENTS.filter(
|
|
2193
|
-
(requirement) => requirement.appliesTo.some((scope) => scopes.has(scope))
|
|
2194
|
-
);
|
|
2195
|
-
}
|
|
2196
|
-
function auditApplicationRequirements(applicationOrId) {
|
|
2197
|
-
const application = typeof applicationOrId === "string" ? fallbackApplication(applicationOrId) : applicationOrId;
|
|
2198
|
-
const scopes = applicationRequirementScopes(application);
|
|
2199
|
-
const findings = requirementsForApplication(application).map(
|
|
2200
|
-
(requirement) => evaluateRequirement(application, requirement)
|
|
2201
|
-
);
|
|
2202
|
-
return {
|
|
2203
|
-
version: APPLICATION_REQUIREMENTS_VERSION,
|
|
2204
|
-
application,
|
|
2205
|
-
scopes,
|
|
2206
|
-
score: scoreFindings(findings),
|
|
2207
|
-
findings
|
|
2208
|
-
};
|
|
2209
|
-
}
|
|
2210
|
-
function auditApplicationEntries(applications) {
|
|
2211
|
-
return buildAuditReport(
|
|
2212
|
-
applications.map((application) => auditApplicationRequirements(application))
|
|
2213
|
-
);
|
|
309
|
+
};
|
|
310
|
+
function hostnameHasSuffix(hostname, suffix) {
|
|
311
|
+
return hostname === suffix || hostname.endsWith(`.${suffix}`);
|
|
2214
312
|
}
|
|
2215
|
-
function
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
applicationName: audit.application.name,
|
|
2220
|
-
requirementId: finding2.requirementId,
|
|
2221
|
-
title: finding2.title,
|
|
2222
|
-
status: finding2.status,
|
|
2223
|
-
reason: finding2.reason,
|
|
2224
|
-
nextActions: finding2.nextActions
|
|
2225
|
-
}))
|
|
2226
|
-
).sort((a, b) => {
|
|
2227
|
-
const statusOrder = statusRank(a.status) - statusRank(b.status);
|
|
2228
|
-
if (statusOrder !== 0) return statusOrder;
|
|
2229
|
-
const appOrder = a.applicationId.localeCompare(b.applicationId);
|
|
2230
|
-
if (appOrder !== 0) return appOrder;
|
|
2231
|
-
return a.requirementId.localeCompare(b.requirementId);
|
|
2232
|
-
});
|
|
2233
|
-
return {
|
|
2234
|
-
version: APPLICATION_REQUIREMENTS_VERSION,
|
|
2235
|
-
applications,
|
|
2236
|
-
migrationQueue
|
|
2237
|
-
};
|
|
313
|
+
function workersDevAccountMatches(hostname, account) {
|
|
314
|
+
if (!hostname.endsWith(".workers.dev")) return false;
|
|
315
|
+
const labels = hostname.slice(0, -".workers.dev".length).split(".");
|
|
316
|
+
return labels.length >= 2 && labels.at(-1) === account;
|
|
2238
317
|
}
|
|
2239
|
-
function
|
|
2240
|
-
const
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
if (application.packageName) evidence.push(`package=${application.packageName}`);
|
|
2244
|
-
if (application.productionUrl) evidence.push(`productionUrl=${application.productionUrl}`);
|
|
2245
|
-
if (application.deployTargets.length > 0) {
|
|
2246
|
-
evidence.push(`deployTargets=${application.deployTargets.length}`);
|
|
2247
|
-
}
|
|
2248
|
-
if (application.services.length > 0) {
|
|
2249
|
-
evidence.push(`services=${application.services.join(",")}`);
|
|
2250
|
-
}
|
|
2251
|
-
if (evidence.length === 0) {
|
|
2252
|
-
return finding(requirement, "missing", "No package, production URL, deploy target, or service metadata is available.", evidence);
|
|
2253
|
-
}
|
|
2254
|
-
return finding(requirement, "met", "Catalog metadata exists for this application.", evidence);
|
|
2255
|
-
}
|
|
2256
|
-
case "canonical-docs":
|
|
2257
|
-
return finding(
|
|
2258
|
-
requirement,
|
|
2259
|
-
"needs_review",
|
|
2260
|
-
"Catalog metadata cannot verify matching TopoloDocs system and handbook coverage.",
|
|
2261
|
-
evidence
|
|
2262
|
-
);
|
|
2263
|
-
case "shared-auth-boundary":
|
|
2264
|
-
return finding(
|
|
2265
|
-
requirement,
|
|
2266
|
-
"needs_review",
|
|
2267
|
-
"Catalog metadata cannot verify that implementation uses shared Topolo auth packages and derives org from credentials.",
|
|
2268
|
-
evidence
|
|
2269
|
-
);
|
|
2270
|
-
case "shared-shell-and-launcher":
|
|
2271
|
-
return finding(
|
|
2272
|
-
requirement,
|
|
2273
|
-
"needs_review",
|
|
2274
|
-
"Catalog metadata cannot verify shared shell, launcher, and account menu adoption.",
|
|
2275
|
-
evidence
|
|
2276
|
-
);
|
|
2277
|
-
case "app-registration-and-scopes": {
|
|
2278
|
-
if (application.services.length > 0) {
|
|
2279
|
-
evidence.push(`services=${application.services.join(",")}`);
|
|
2280
|
-
return finding(
|
|
2281
|
-
requirement,
|
|
2282
|
-
"partial",
|
|
2283
|
-
"SDK service discovery exists, but Auth catalog permissions, role bundles, and API-key scopes still need confirmation.",
|
|
2284
|
-
evidence
|
|
2285
|
-
);
|
|
2286
|
-
}
|
|
2287
|
-
return finding(requirement, "missing", "No SDK app API id is registered for this callable API surface.", evidence);
|
|
2288
|
-
}
|
|
2289
|
-
case "organization-scoped-data":
|
|
2290
|
-
return finding(
|
|
2291
|
-
requirement,
|
|
2292
|
-
"needs_review",
|
|
2293
|
-
"Catalog metadata cannot verify backend tenant filters, storage keys, or Durable Object boundaries.",
|
|
2294
|
-
evidence
|
|
2295
|
-
);
|
|
2296
|
-
case "cloudflare-deployment-contract": {
|
|
2297
|
-
if (application.deployTargets.length < 1) {
|
|
2298
|
-
return finding(requirement, "missing", "No CloudControl deploy targets are registered.", evidence);
|
|
2299
|
-
}
|
|
2300
|
-
evidence.push(`deployTargets=${application.deployTargets.length}`);
|
|
2301
|
-
if (application.productionUrl) evidence.push(`productionUrl=${application.productionUrl}`);
|
|
2302
|
-
return finding(requirement, "met", "CloudControl deploy target metadata is present.", evidence);
|
|
2303
|
-
}
|
|
2304
|
-
case "agent-and-operator-surfaces": {
|
|
2305
|
-
const appId = application.id;
|
|
2306
|
-
if (application.services.length > 0) {
|
|
2307
|
-
evidence.push(`services=${application.services.join(",")}`);
|
|
2308
|
-
return finding(requirement, "partial", "SDK service discovery exists; typed SDK/CLI/MCP workflow coverage should be checked as APIs stabilize.", evidence);
|
|
2309
|
-
}
|
|
2310
|
-
if (appId === "cli" || appId === "mcp" || appId === "cloudcontrol") {
|
|
2311
|
-
evidence.push(`tooling=${appId}`);
|
|
2312
|
-
return finding(requirement, "met", "This application is itself an agent/operator surface.", evidence);
|
|
2313
|
-
}
|
|
2314
|
-
return finding(requirement, "missing", "No SDK service or tooling surface is registered.", evidence);
|
|
2315
|
-
}
|
|
2316
|
-
case "native-dashboard-widget":
|
|
2317
|
-
return finding(
|
|
2318
|
-
requirement,
|
|
2319
|
-
"needs_review",
|
|
2320
|
-
"Catalog metadata cannot verify app-owned /api/widget implementation details.",
|
|
2321
|
-
evidence
|
|
2322
|
-
);
|
|
2323
|
-
case "observability-and-audit":
|
|
2324
|
-
return finding(
|
|
2325
|
-
requirement,
|
|
2326
|
-
"needs_review",
|
|
2327
|
-
"Catalog metadata cannot verify runtime request IDs, client labels, or log redaction.",
|
|
2328
|
-
evidence
|
|
2329
|
-
);
|
|
2330
|
-
case "verification-gates":
|
|
2331
|
-
return finding(
|
|
2332
|
-
requirement,
|
|
2333
|
-
"needs_review",
|
|
2334
|
-
"Catalog metadata cannot verify package checks, docs validation, or smoke evidence.",
|
|
2335
|
-
evidence
|
|
2336
|
-
);
|
|
2337
|
-
default:
|
|
2338
|
-
return finding(requirement, "needs_review", "No automated evaluator exists for this requirement yet.", evidence);
|
|
318
|
+
function originMatchesEnvironment(url, environment) {
|
|
319
|
+
const hostname = url.hostname.toLowerCase();
|
|
320
|
+
if (environment === "local") {
|
|
321
|
+
return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
2339
322
|
}
|
|
323
|
+
if (url.protocol !== "https:") return false;
|
|
324
|
+
if (environment === "production") {
|
|
325
|
+
return hostnameHasSuffix(hostname, "topolo.app") || hostnameHasSuffix(hostname, "topolo.io") || workersDevAccountMatches(hostname, "topolo");
|
|
326
|
+
}
|
|
327
|
+
if (environment === "staging") {
|
|
328
|
+
return hostnameHasSuffix(hostname, "stg.topolo.us") || workersDevAccountMatches(hostname, "topolo-staging");
|
|
329
|
+
}
|
|
330
|
+
return hostnameHasSuffix(hostname, "topolo.dev") || workersDevAccountMatches(hostname, "topolo-development");
|
|
2340
331
|
}
|
|
2341
|
-
function
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
332
|
+
function assertTopoloServiceUrlEnvironment(service, value, environment) {
|
|
333
|
+
let url;
|
|
334
|
+
try {
|
|
335
|
+
url = new URL(value);
|
|
336
|
+
} catch {
|
|
337
|
+
throw new TopoloSdkError("invalid_service_url", `Invalid URL configured for Topolo service "${service}".`);
|
|
338
|
+
}
|
|
339
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !originMatchesEnvironment(url, environment)) {
|
|
340
|
+
throw new TopoloSdkError(
|
|
341
|
+
"environment_mismatch",
|
|
342
|
+
`Refusing Topolo service "${service}": ${url.origin} does not belong to ${environment}.`
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
return url.origin;
|
|
2350
346
|
}
|
|
2351
|
-
function
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
return {
|
|
2359
|
-
met,
|
|
2360
|
-
partial,
|
|
2361
|
-
missing,
|
|
2362
|
-
needsReview,
|
|
2363
|
-
total,
|
|
2364
|
-
percent: total === 0 ? 100 : Math.round(weighted / total * 100)
|
|
2365
|
-
};
|
|
347
|
+
function topoloServiceOrigins(serviceUrls, environment) {
|
|
348
|
+
return Object.fromEntries(
|
|
349
|
+
Object.entries(serviceUrls ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(([service, value]) => [
|
|
350
|
+
service,
|
|
351
|
+
assertTopoloServiceUrlEnvironment(service, value, environment)
|
|
352
|
+
])
|
|
353
|
+
);
|
|
2366
354
|
}
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
355
|
+
var APP_API_URLS = {
|
|
356
|
+
auth: "https://auth.topolo.app",
|
|
357
|
+
developers: "https://developers.topolo.app"
|
|
358
|
+
};
|
|
359
|
+
function friendlyAppId(entry) {
|
|
360
|
+
for (const value of [entry.slug, entry.name, entry.appId]) {
|
|
361
|
+
if (typeof value !== "string" || !value.trim()) continue;
|
|
362
|
+
const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
|
|
363
|
+
const withoutPrefix = normalized.replace(/^(?:app[_-])?topolo[-_]+/, "");
|
|
364
|
+
if (withoutPrefix) return withoutPrefix;
|
|
365
|
+
}
|
|
366
|
+
return entry.appId;
|
|
2371
367
|
}
|
|
2372
368
|
var DEFAULT_APP_URLS = APP_API_URLS;
|
|
2373
369
|
function resolveCatalogServiceUrl(entry, options = {}) {
|
|
@@ -2851,6 +847,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2851
847
|
credential;
|
|
2852
848
|
agent;
|
|
2853
849
|
serviceUrls;
|
|
850
|
+
environment;
|
|
2854
851
|
appIds;
|
|
2855
852
|
requireConfirmForWrites;
|
|
2856
853
|
timeoutMs;
|
|
@@ -2879,7 +876,8 @@ var TopoloClient = class _TopoloClient {
|
|
|
2879
876
|
if (!options.agent?.clientName) throw new TopoloAuthError("agent.clientName is required");
|
|
2880
877
|
this.credential = options.credential;
|
|
2881
878
|
this.agent = options.agent;
|
|
2882
|
-
this.
|
|
879
|
+
this.environment = options.environment;
|
|
880
|
+
this.serviceUrls = options.environment ? topoloServiceOrigins(options.serviceUrls, options.environment) : options.serviceUrls;
|
|
2883
881
|
this.appIds = options.appIds;
|
|
2884
882
|
this.requireConfirmForWrites = options.requireConfirmForWrites !== false;
|
|
2885
883
|
this.timeoutMs = options.timeoutMs ?? 3e4;
|
|
@@ -2908,9 +906,15 @@ var TopoloClient = class _TopoloClient {
|
|
|
2908
906
|
);
|
|
2909
907
|
}
|
|
2910
908
|
await this.loadCurrentCredential();
|
|
2911
|
-
const resolvedService = opts.baseUrl ? { serviceKey: normalizeLookupKey(opts.service), baseUrl:
|
|
909
|
+
const resolvedService = opts.baseUrl ? { serviceKey: normalizeLookupKey(opts.service), baseUrl: this.validatedServiceOrigin(opts.service, opts.baseUrl) } : await this.resolveRequestService(opts.service);
|
|
2912
910
|
const baseUrl = resolvedService.baseUrl;
|
|
2913
911
|
const url = new URL(opts.path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
912
|
+
if (url.origin !== new URL(baseUrl).origin) {
|
|
913
|
+
throw new TopoloSdkError(
|
|
914
|
+
"service_path_origin_mismatch",
|
|
915
|
+
`Refusing Topolo request path because it changes the resolved ${resolvedService.serviceKey || opts.service} service origin.`
|
|
916
|
+
);
|
|
917
|
+
}
|
|
2914
918
|
if (opts.query) {
|
|
2915
919
|
for (const [k, v] of Object.entries(opts.query)) {
|
|
2916
920
|
if (v !== void 0) url.searchParams.set(k, String(v));
|
|
@@ -2981,7 +985,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2981
985
|
url: urlStr,
|
|
2982
986
|
requestId,
|
|
2983
987
|
durationMs: Date.now() - startedAt,
|
|
2984
|
-
error:
|
|
988
|
+
error: "network_error"
|
|
2985
989
|
});
|
|
2986
990
|
throw err;
|
|
2987
991
|
}
|
|
@@ -2990,6 +994,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2990
994
|
if (!res.ok) {
|
|
2991
995
|
const durationMs = Date.now() - startedAt;
|
|
2992
996
|
const message = describeError(parsed, `HTTP ${res.status}`);
|
|
997
|
+
const publicCode = extractTopoloServiceErrorCode(parsed) ?? "http_error";
|
|
2993
998
|
this.emit({
|
|
2994
999
|
phase: "error",
|
|
2995
1000
|
method,
|
|
@@ -2998,7 +1003,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2998
1003
|
url: urlStr,
|
|
2999
1004
|
requestId,
|
|
3000
1005
|
durationMs,
|
|
3001
|
-
error:
|
|
1006
|
+
error: publicCode,
|
|
3002
1007
|
status: res.status
|
|
3003
1008
|
});
|
|
3004
1009
|
const errorContext = {
|
|
@@ -3006,17 +1011,21 @@ var TopoloClient = class _TopoloClient {
|
|
|
3006
1011
|
body: parsed,
|
|
3007
1012
|
service: resolvedService.serviceKey,
|
|
3008
1013
|
path: opts.path,
|
|
3009
|
-
requestId: res.headers.get("X-Topolo-Request-Id")
|
|
3010
|
-
retryAfter: res.headers.get("Retry-After")
|
|
1014
|
+
requestId: canonicalTopoloRequestId(res.headers.get("X-Topolo-Request-Id")) ?? requestId,
|
|
1015
|
+
retryAfter: canonicalTopoloRetryAfter(res.headers.get("Retry-After"))
|
|
3011
1016
|
};
|
|
3012
1017
|
if (res.status === 401) {
|
|
3013
|
-
throw new TopoloAuthError(
|
|
1018
|
+
throw new TopoloAuthError(
|
|
1019
|
+
describeError(parsed, "Unauthorized"),
|
|
1020
|
+
extractTopoloServiceErrorCode(parsed) ?? "auth_error",
|
|
1021
|
+
errorContext
|
|
1022
|
+
);
|
|
3014
1023
|
}
|
|
3015
1024
|
if (res.status === 403) {
|
|
3016
1025
|
throw new TopoloPermissionError(
|
|
3017
1026
|
describeError(parsed, "Permission denied"),
|
|
3018
1027
|
extractRequired(parsed),
|
|
3019
|
-
errorContext
|
|
1028
|
+
{ ...errorContext, code: extractTopoloServiceErrorCode(parsed) ?? "permission_denied" }
|
|
3020
1029
|
);
|
|
3021
1030
|
}
|
|
3022
1031
|
throw new TopoloHttpError(
|
|
@@ -3066,6 +1075,17 @@ var TopoloClient = class _TopoloClient {
|
|
|
3066
1075
|
});
|
|
3067
1076
|
return this.refreshInFlight;
|
|
3068
1077
|
}
|
|
1078
|
+
validatedServiceOrigin(service, value) {
|
|
1079
|
+
const origin = requireHttpOrigin(value);
|
|
1080
|
+
return this.environment ? assertTopoloServiceUrlEnvironment(service, origin, this.environment) : origin;
|
|
1081
|
+
}
|
|
1082
|
+
validatedActionEntry(entry) {
|
|
1083
|
+
if (!entry.apiBaseUrl) return entry;
|
|
1084
|
+
return {
|
|
1085
|
+
...entry,
|
|
1086
|
+
apiBaseUrl: this.validatedServiceOrigin(entry.appSlug || entry.appId, entry.apiBaseUrl)
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
3069
1089
|
async resolveRequestService(service) {
|
|
3070
1090
|
const serviceKey = normalizeLookupKey(service);
|
|
3071
1091
|
if (!serviceKey) {
|
|
@@ -3073,17 +1093,20 @@ var TopoloClient = class _TopoloClient {
|
|
|
3073
1093
|
}
|
|
3074
1094
|
if (serviceKey === "auth" || serviceKey === "developers") {
|
|
3075
1095
|
const overrideUrl = resolveConfiguredServiceUrl(serviceKey, this.serviceUrls);
|
|
3076
|
-
if (overrideUrl) return { serviceKey, baseUrl: overrideUrl };
|
|
1096
|
+
if (overrideUrl) return { serviceKey, baseUrl: this.validatedServiceOrigin(serviceKey, overrideUrl) };
|
|
3077
1097
|
if (serviceKey === "developers") {
|
|
3078
1098
|
const derived = deriveDevelopersFromAuth(resolveConfiguredServiceUrl("auth", this.serviceUrls));
|
|
3079
|
-
if (derived) return { serviceKey, baseUrl: derived };
|
|
1099
|
+
if (derived) return { serviceKey, baseUrl: this.validatedServiceOrigin(serviceKey, derived) };
|
|
3080
1100
|
}
|
|
3081
|
-
return {
|
|
1101
|
+
return {
|
|
1102
|
+
serviceKey,
|
|
1103
|
+
baseUrl: this.validatedServiceOrigin(serviceKey, DEFAULT_APP_URLS[serviceKey])
|
|
1104
|
+
};
|
|
3082
1105
|
}
|
|
3083
1106
|
const live = await this.resolveLiveCallableService(serviceKey);
|
|
3084
1107
|
if (live) {
|
|
3085
1108
|
const overrideUrl = resolveConfiguredServiceUrl(serviceKey, this.serviceUrls);
|
|
3086
|
-
return overrideUrl ? { ...live, baseUrl: overrideUrl } : live;
|
|
1109
|
+
return overrideUrl ? { ...live, baseUrl: this.validatedServiceOrigin(serviceKey, overrideUrl) } : { ...live, baseUrl: this.validatedServiceOrigin(serviceKey, live.baseUrl) };
|
|
3087
1110
|
}
|
|
3088
1111
|
throw new TopoloSdkError(
|
|
3089
1112
|
"unknown_service",
|
|
@@ -3097,10 +1120,10 @@ var TopoloClient = class _TopoloClient {
|
|
|
3097
1120
|
serviceKey,
|
|
3098
1121
|
overrides: this.serviceUrls
|
|
3099
1122
|
});
|
|
3100
|
-
if (baseUrl) return { serviceKey, baseUrl };
|
|
1123
|
+
if (baseUrl) return { serviceKey, baseUrl: this.validatedServiceOrigin(serviceKey, baseUrl) };
|
|
3101
1124
|
const actionPage = await this.searchActions({ service: match.appId, limit: 1 }).catch(() => null);
|
|
3102
1125
|
const actionBaseUrl = actionPage?.actions.find((action) => action.apiBaseUrl)?.apiBaseUrl ?? null;
|
|
3103
|
-
return actionBaseUrl ? { serviceKey, baseUrl:
|
|
1126
|
+
return actionBaseUrl ? { serviceKey, baseUrl: this.validatedServiceOrigin(serviceKey, actionBaseUrl) } : null;
|
|
3104
1127
|
}
|
|
3105
1128
|
/**
|
|
3106
1129
|
* Resolve a service-slug → opaque app-id via caller config or TopoloAuth.
|
|
@@ -3120,7 +1143,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
3120
1143
|
if (!normalized) return null;
|
|
3121
1144
|
const configuredAppId = resolveConfiguredAppId(normalized, this.appIds);
|
|
3122
1145
|
if (configuredAppId) return configuredAppId;
|
|
3123
|
-
const authBase = resolveServiceUrl("developers", this.serviceUrls);
|
|
1146
|
+
const authBase = this.validatedServiceOrigin("developers", resolveServiceUrl("developers", this.serviceUrls));
|
|
3124
1147
|
const cacheKey = `${authBase}|${normalized}`;
|
|
3125
1148
|
const now = Date.now();
|
|
3126
1149
|
const cached = _TopoloClient.appIdCache.get(cacheKey);
|
|
@@ -3296,7 +1319,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
3296
1319
|
"TopoloAuth did not return an action catalog for the current credential."
|
|
3297
1320
|
);
|
|
3298
1321
|
}
|
|
3299
|
-
const entries = raw.map((item) => normalizeActionCatalogEntry(item)).filter((entry) => Boolean(entry));
|
|
1322
|
+
const entries = raw.map((item) => normalizeActionCatalogEntry(item)).filter((entry) => Boolean(entry)).map((entry) => this.validatedActionEntry(entry));
|
|
3300
1323
|
return {
|
|
3301
1324
|
actions: entries,
|
|
3302
1325
|
page: normalizeCatalogPage(
|
|
@@ -3361,7 +1384,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
3361
1384
|
const raw = res.data?.action ?? res.action;
|
|
3362
1385
|
const match = normalizeActionCatalogEntry(raw);
|
|
3363
1386
|
if (!match) throw new TopoloSdkError("unknown_action", `Unknown or unavailable Topolo action "${action}".`);
|
|
3364
|
-
return match;
|
|
1387
|
+
return this.validatedActionEntry(match);
|
|
3365
1388
|
}
|
|
3366
1389
|
async callAction(action, input = {}, options = {}) {
|
|
3367
1390
|
const entry = await this.getAction(action);
|
|
@@ -3438,6 +1461,25 @@ var TopoloClient = class _TopoloClient {
|
|
|
3438
1461
|
}
|
|
3439
1462
|
async planAction(action, input = {}, options = {}) {
|
|
3440
1463
|
const entry = await this.getAction(action);
|
|
1464
|
+
return this.planResolvedAction(entry, input, options);
|
|
1465
|
+
}
|
|
1466
|
+
async prepareAction(action, input, options = {}) {
|
|
1467
|
+
const entry = await this.getAction(action);
|
|
1468
|
+
const example = (entry.agentContract?.examples ?? []).find((candidate) => Boolean(candidate?.input) && typeof candidate.input === "object" && !Array.isArray(candidate.input) && validateTopoloActionInput(entry.inputSchema, candidate.input).valid) ?? null;
|
|
1469
|
+
const selectedInput = input ?? example?.input ?? {};
|
|
1470
|
+
const plan = this.planResolvedAction(entry, selectedInput, options);
|
|
1471
|
+
return {
|
|
1472
|
+
action: entry,
|
|
1473
|
+
example,
|
|
1474
|
+
input: selectedInput,
|
|
1475
|
+
inputSource: input ? "provided" : example ? "example" : "empty",
|
|
1476
|
+
missingFields: [...new Set(
|
|
1477
|
+
plan.validation.errors.filter((issue) => issue.code === "required").map((issue) => issue.path)
|
|
1478
|
+
)],
|
|
1479
|
+
plan
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
planResolvedAction(entry, input, options) {
|
|
3441
1483
|
const validation = validateTopoloActionInput(entry.inputSchema, input);
|
|
3442
1484
|
const agentAccess = entry.agentAccess ?? (entry.destructive ? "off" : entry.readOnly && !entry.requiresConfirmation ? "auto" : "confirm");
|
|
3443
1485
|
const confirmationRequired = agentAccess === "confirm" || entry.requiresConfirmation || entry.destructive;
|
|
@@ -3514,28 +1556,6 @@ var TopoloClient = class _TopoloClient {
|
|
|
3514
1556
|
confirm: options.confirm === true || entry.readOnly && !entry.destructive && !entry.requiresConfirmation
|
|
3515
1557
|
});
|
|
3516
1558
|
}
|
|
3517
|
-
/**
|
|
3518
|
-
* @deprecated Static fallback snapshot only. Use `searchServices()` (the
|
|
3519
|
-
* live credential-scoped registry) for anything that decides behavior; this
|
|
3520
|
-
* exists for offline packaging audits when the registry is unreachable.
|
|
3521
|
-
*/
|
|
3522
|
-
listBundledServices() {
|
|
3523
|
-
const apps = Object.values(APPLICATIONS);
|
|
3524
|
-
return apps.map((app) => ({
|
|
3525
|
-
appId: app.id,
|
|
3526
|
-
slug: app.id,
|
|
3527
|
-
name: app.name ?? app.id,
|
|
3528
|
-
description: null,
|
|
3529
|
-
apiBaseUrl: null,
|
|
3530
|
-
launchUrl: app.productionUrl ?? null,
|
|
3531
|
-
status: "bundled",
|
|
3532
|
-
surfaceKind: "application",
|
|
3533
|
-
launchable: Boolean(app.productionUrl),
|
|
3534
|
-
permissions: [],
|
|
3535
|
-
apiKeyResource: null,
|
|
3536
|
-
apiKeyResources: []
|
|
3537
|
-
}));
|
|
3538
|
-
}
|
|
3539
1559
|
/**
|
|
3540
1560
|
* Introspect the current credential. Returns the caller's resolved identity,
|
|
3541
1561
|
* organization, and permission set. Used by CLI `whoami` and by MCP to gate
|
|
@@ -3786,6 +1806,7 @@ function normalizeActionCatalogEntry(item) {
|
|
|
3786
1806
|
path,
|
|
3787
1807
|
permissionName: pickActionString(a.permission_name) ?? pickActionString(a.permissionName) ?? "",
|
|
3788
1808
|
requiredPermission: pickActionString(a.required_permission) ?? pickActionString(a.requiredPermission) ?? "",
|
|
1809
|
+
principalAccess: pickPrincipalAccess(a.principal_access ?? a.principalAccess),
|
|
3789
1810
|
authorizationMode: pickAuthorizationMode(a.authorization_mode ?? a.authorizationMode),
|
|
3790
1811
|
allowedServices: stringArray(a.allowed_services ?? a.allowedServices),
|
|
3791
1812
|
inputSchema: plainObject(a.input_schema ?? a.inputSchema),
|
|
@@ -3802,6 +1823,9 @@ function normalizeActionCatalogEntry(item) {
|
|
|
3802
1823
|
function isHttpMethod(value) {
|
|
3803
1824
|
return value === "GET" || value === "POST" || value === "PUT" || value === "PATCH" || value === "DELETE";
|
|
3804
1825
|
}
|
|
1826
|
+
function pickPrincipalAccess(value) {
|
|
1827
|
+
return value === "platform_admin" || value === "platform_super_admin" ? value : null;
|
|
1828
|
+
}
|
|
3805
1829
|
function pickActionString(value) {
|
|
3806
1830
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
3807
1831
|
}
|
|
@@ -3895,7 +1919,16 @@ function pickString(value) {
|
|
|
3895
1919
|
function extractRequired(body) {
|
|
3896
1920
|
if (body && typeof body === "object") {
|
|
3897
1921
|
const maybe = body;
|
|
3898
|
-
if (Array.isArray(maybe.required)) return maybe.required
|
|
1922
|
+
if (Array.isArray(maybe.required)) return canonicalTopoloRequired(maybe.required);
|
|
1923
|
+
for (const nested of [maybe.details, maybe.error]) {
|
|
1924
|
+
if (!nested || typeof nested !== "object" || Array.isArray(nested)) continue;
|
|
1925
|
+
const record = nested;
|
|
1926
|
+
if (Array.isArray(record.required)) return canonicalTopoloRequired(record.required);
|
|
1927
|
+
if (record.details && typeof record.details === "object" && !Array.isArray(record.details)) {
|
|
1928
|
+
const required = record.details.required;
|
|
1929
|
+
if (Array.isArray(required)) return canonicalTopoloRequired(required);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
3899
1932
|
}
|
|
3900
1933
|
return [];
|
|
3901
1934
|
}
|
|
@@ -3943,17 +1976,72 @@ function compactActionPlan(plan) {
|
|
|
3943
1976
|
nextActionIds: plan.nextActionIds
|
|
3944
1977
|
};
|
|
3945
1978
|
}
|
|
3946
|
-
function
|
|
1979
|
+
function requiredContextText(value, field) {
|
|
1980
|
+
if (typeof value !== "string" || !value.trim() || value.trim().toLowerCase() === "unknown") {
|
|
1981
|
+
throw new TopoloSdkError("invalid_identity_context", `Effective Topolo ${field} is incomplete.`);
|
|
1982
|
+
}
|
|
1983
|
+
return value.trim();
|
|
1984
|
+
}
|
|
1985
|
+
function createTopoloAgentExecutionContext(input) {
|
|
1986
|
+
if (input.contextKey !== null && input.contextKey !== "personal" && !/^org:[^:]+$/.test(input.contextKey)) {
|
|
1987
|
+
throw new TopoloSdkError("invalid_identity_context", "Effective Topolo context key is invalid.");
|
|
1988
|
+
}
|
|
1989
|
+
const user = Object.freeze({
|
|
1990
|
+
id: requiredContextText(input.identity.user.id, "user id"),
|
|
1991
|
+
email: requiredContextText(input.identity.user.email, "user email"),
|
|
1992
|
+
name: input.identity.user.name?.trim() || null,
|
|
1993
|
+
role: requiredContextText(input.identity.user.role, "user role")
|
|
1994
|
+
});
|
|
1995
|
+
const organization = input.identity.organization ? Object.freeze({
|
|
1996
|
+
id: requiredContextText(input.identity.organization.id, "organization id"),
|
|
1997
|
+
slug: requiredContextText(input.identity.organization.slug, "organization slug"),
|
|
1998
|
+
name: requiredContextText(input.identity.organization.name, "organization name")
|
|
1999
|
+
}) : null;
|
|
2000
|
+
if (input.contextKey === "personal" && organization) {
|
|
2001
|
+
throw new TopoloSdkError("identity_context_mismatch", "Personal Topolo context resolved an organization.");
|
|
2002
|
+
}
|
|
2003
|
+
if (input.contextKey?.startsWith("org:")) {
|
|
2004
|
+
const expectedOrganizationId = input.contextKey.slice("org:".length);
|
|
2005
|
+
if (!organization || organization.id !== expectedOrganizationId) {
|
|
2006
|
+
throw new TopoloSdkError("identity_context_mismatch", "Pinned Topolo organization does not match the effective identity.");
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
return Object.freeze({
|
|
2010
|
+
environment: input.environment,
|
|
2011
|
+
contextKey: input.contextKey,
|
|
2012
|
+
user,
|
|
2013
|
+
organization,
|
|
2014
|
+
serviceOrigins: Object.freeze(topoloServiceOrigins(input.serviceUrls, input.environment))
|
|
2015
|
+
});
|
|
2016
|
+
}
|
|
2017
|
+
function compactActionPreparationCore(preparation) {
|
|
2018
|
+
const example = preparation.example ? { title: preparation.example.title ?? null, input: preparation.example.input } : null;
|
|
2019
|
+
const effectiveInput = preparation.inputSource === "example" ? { source: "example" } : { source: preparation.inputSource, value: preparation.input };
|
|
3947
2020
|
return {
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
2021
|
+
action: {
|
|
2022
|
+
id: preparation.action.actionId,
|
|
2023
|
+
title: preparation.action.title,
|
|
2024
|
+
docsUrl: preparation.action.agentContract?.docsUrl ?? null
|
|
2025
|
+
},
|
|
2026
|
+
example,
|
|
2027
|
+
input: effectiveInput,
|
|
2028
|
+
missingFields: preparation.missingFields,
|
|
2029
|
+
valid: preparation.plan.validation.valid,
|
|
2030
|
+
errors: preparation.plan.validation.errors,
|
|
2031
|
+
request: preparation.plan.request,
|
|
2032
|
+
confirmation: {
|
|
2033
|
+
required: preparation.plan.confirmationRequired,
|
|
2034
|
+
provided: preparation.plan.confirmationProvided
|
|
2035
|
+
},
|
|
2036
|
+
executable: preparation.plan.executable,
|
|
2037
|
+
verification: preparation.plan.verification,
|
|
2038
|
+
rollbackActionId: preparation.plan.rollbackActionId
|
|
3955
2039
|
};
|
|
3956
2040
|
}
|
|
2041
|
+
function compactActionPreparation(preparation, context) {
|
|
2042
|
+
const compact = compactActionPreparationCore(preparation);
|
|
2043
|
+
return context ? { context, ...compact } : compact;
|
|
2044
|
+
}
|
|
3957
2045
|
var CrmModule = class {
|
|
3958
2046
|
constructor(client) {
|
|
3959
2047
|
this.client = client;
|
|
@@ -4081,7 +2169,7 @@ function createTopolo(options) {
|
|
|
4081
2169
|
};
|
|
4082
2170
|
}
|
|
4083
2171
|
|
|
4084
|
-
//
|
|
2172
|
+
// ../../../public-platform-tooling-boundary-20260813/packages/topolo-sdk/dist/node/credential-store.js
|
|
4085
2173
|
import { mkdir, readFile, writeFile, chmod } from "fs/promises";
|
|
4086
2174
|
import { existsSync } from "fs";
|
|
4087
2175
|
import { homedir, platform } from "os";
|
|
@@ -4094,6 +2182,90 @@ var TopoloSdkError2 = class extends Error {
|
|
|
4094
2182
|
this.code = code;
|
|
4095
2183
|
}
|
|
4096
2184
|
};
|
|
2185
|
+
var PUBLIC_ERROR_CODES2 = /* @__PURE__ */ new Set([
|
|
2186
|
+
"action_app_identity_unavailable",
|
|
2187
|
+
"action_authentication_required",
|
|
2188
|
+
"action_catalog_summary_unavailable",
|
|
2189
|
+
"action_catalog_unavailable",
|
|
2190
|
+
"action_contract_mismatch",
|
|
2191
|
+
"action_permission_required",
|
|
2192
|
+
"action_runtime_authorization_required",
|
|
2193
|
+
"action_service_not_allowed",
|
|
2194
|
+
"auth_error",
|
|
2195
|
+
"authentication_required",
|
|
2196
|
+
"authorization_pending",
|
|
2197
|
+
"bad_sig",
|
|
2198
|
+
"confirmation_required",
|
|
2199
|
+
"dependency_unavailable",
|
|
2200
|
+
"environment_mismatch",
|
|
2201
|
+
"environment_service_url_required",
|
|
2202
|
+
"expired_token",
|
|
2203
|
+
"forbidden",
|
|
2204
|
+
"http_error",
|
|
2205
|
+
"immutable_drift",
|
|
2206
|
+
"integration_dependency_unavailable",
|
|
2207
|
+
"integration_misconfigured",
|
|
2208
|
+
"invalid_action",
|
|
2209
|
+
"invalid_action_input",
|
|
2210
|
+
"invalid_grant",
|
|
2211
|
+
"invalid_input",
|
|
2212
|
+
"invalid_token",
|
|
2213
|
+
"mcp_error",
|
|
2214
|
+
"membership_required",
|
|
2215
|
+
"network_error",
|
|
2216
|
+
"organization_context_required",
|
|
2217
|
+
"organization_enrollment_required",
|
|
2218
|
+
"permission_denied",
|
|
2219
|
+
"rate_limited",
|
|
2220
|
+
"request_failed",
|
|
2221
|
+
"service_catalog_unavailable",
|
|
2222
|
+
"service_path_origin_mismatch",
|
|
2223
|
+
"service_required",
|
|
2224
|
+
"slow_down",
|
|
2225
|
+
"state_conflict",
|
|
2226
|
+
"unauthorized",
|
|
2227
|
+
"unknown_action",
|
|
2228
|
+
"unknown_service"
|
|
2229
|
+
]);
|
|
2230
|
+
var ERROR_CODE_PATTERN2 = /^[a-z][a-z0-9_]{0,63}$/;
|
|
2231
|
+
var TOPOLO_REQUEST_ID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
2232
|
+
var CLOUDFLARE_RAY_ID_PATTERN2 = /^[0-9a-f]{16}-[A-Z]{3}$/;
|
|
2233
|
+
var PERMISSION_PATTERN2 = /^[a-z][a-z0-9_-]{0,63}(?:\.[a-z][a-z0-9_-]{0,63})*:(?:[a-z][a-z0-9_-]{0,63}|\*)$/;
|
|
2234
|
+
var RETRY_AFTER_DATE_PATTERN2 = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
|
|
2235
|
+
var PUBLIC_DETAIL_SOURCES2 = /* @__PURE__ */ new Set([
|
|
2236
|
+
"api_key_introspection",
|
|
2237
|
+
"api_key_validation",
|
|
2238
|
+
"auth_context",
|
|
2239
|
+
"auth_validate",
|
|
2240
|
+
"catalog",
|
|
2241
|
+
"credential",
|
|
2242
|
+
"runtime",
|
|
2243
|
+
"service_token_provider",
|
|
2244
|
+
"service_verify_key",
|
|
2245
|
+
"token_validation",
|
|
2246
|
+
"upstream"
|
|
2247
|
+
]);
|
|
2248
|
+
function canonicalTopoloErrorCode2(value) {
|
|
2249
|
+
if (typeof value !== "string") return null;
|
|
2250
|
+
const candidate = value.trim().toLowerCase();
|
|
2251
|
+
if (!ERROR_CODE_PATTERN2.test(candidate) || !PUBLIC_ERROR_CODES2.has(candidate)) return null;
|
|
2252
|
+
return candidate;
|
|
2253
|
+
}
|
|
2254
|
+
function canonicalTopoloRequestId2(value) {
|
|
2255
|
+
if (typeof value !== "string") return void 0;
|
|
2256
|
+
const candidate = value.trim();
|
|
2257
|
+
return TOPOLO_REQUEST_ID_PATTERN2.test(candidate) || CLOUDFLARE_RAY_ID_PATTERN2.test(candidate) ? candidate : void 0;
|
|
2258
|
+
}
|
|
2259
|
+
function canonicalTopoloRetryAfter2(value) {
|
|
2260
|
+
if (typeof value !== "string") return void 0;
|
|
2261
|
+
const candidate = value.trim();
|
|
2262
|
+
if (/^\d{1,10}$/.test(candidate) || RETRY_AFTER_DATE_PATTERN2.test(candidate)) return candidate;
|
|
2263
|
+
return void 0;
|
|
2264
|
+
}
|
|
2265
|
+
function canonicalTopoloRequired2(value) {
|
|
2266
|
+
if (!Array.isArray(value)) return [];
|
|
2267
|
+
return [...new Set(value.filter((item) => typeof item === "string" && PERMISSION_PATTERN2.test(item)))].slice(0, 32);
|
|
2268
|
+
}
|
|
4097
2269
|
var TopoloAuthError2 = class extends TopoloSdkError2 {
|
|
4098
2270
|
status;
|
|
4099
2271
|
body;
|
|
@@ -4101,27 +2273,91 @@ var TopoloAuthError2 = class extends TopoloSdkError2 {
|
|
|
4101
2273
|
path;
|
|
4102
2274
|
requestId;
|
|
4103
2275
|
retryAfter;
|
|
2276
|
+
details;
|
|
4104
2277
|
constructor(message, code = "auth_error", options = {}) {
|
|
4105
|
-
super(code, message);
|
|
2278
|
+
super(canonicalTopoloErrorCode2(code) ?? "auth_error", message);
|
|
4106
2279
|
this.name = "TopoloAuthError";
|
|
4107
2280
|
this.status = options.status ?? 401;
|
|
4108
2281
|
this.body = options.body;
|
|
4109
2282
|
this.service = options.service;
|
|
4110
2283
|
this.path = options.path;
|
|
4111
|
-
this.requestId = options.requestId;
|
|
4112
|
-
this.retryAfter = options.retryAfter;
|
|
2284
|
+
this.requestId = canonicalTopoloRequestId2(options.requestId);
|
|
2285
|
+
this.retryAfter = canonicalTopoloRetryAfter2(options.retryAfter);
|
|
2286
|
+
this.details = extractTopoloServiceErrorDetails2(
|
|
2287
|
+
options.details === void 0 ? options.body : { details: options.details }
|
|
2288
|
+
);
|
|
4113
2289
|
}
|
|
4114
2290
|
};
|
|
4115
|
-
function
|
|
4116
|
-
if (!body || typeof body !== "object" || Array.isArray(body)) return
|
|
2291
|
+
function extractTopoloServiceErrorCode2(body) {
|
|
2292
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
4117
2293
|
const record = body;
|
|
4118
|
-
|
|
2294
|
+
const topLevel = canonicalTopoloErrorCode2(record.code);
|
|
2295
|
+
if (topLevel) return topLevel;
|
|
4119
2296
|
const nested = record.error;
|
|
2297
|
+
const nestedString = canonicalTopoloErrorCode2(nested);
|
|
2298
|
+
if (nestedString) return nestedString;
|
|
4120
2299
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
4121
2300
|
const code = nested.code;
|
|
4122
|
-
|
|
2301
|
+
const nestedCode = canonicalTopoloErrorCode2(code);
|
|
2302
|
+
if (nestedCode) return nestedCode;
|
|
2303
|
+
}
|
|
2304
|
+
return null;
|
|
2305
|
+
}
|
|
2306
|
+
function serviceErrorCode2(body, fallback) {
|
|
2307
|
+
return extractTopoloServiceErrorCode2(body) ?? fallback;
|
|
2308
|
+
}
|
|
2309
|
+
function extractTopoloServiceErrorDetails2(body) {
|
|
2310
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return void 0;
|
|
2311
|
+
const record = body;
|
|
2312
|
+
let candidate = record.details;
|
|
2313
|
+
const nested = record.error;
|
|
2314
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
2315
|
+
const nestedRecord = nested;
|
|
2316
|
+
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
2317
|
+
}
|
|
2318
|
+
if (candidate === void 0) candidate = record;
|
|
2319
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
|
|
2320
|
+
const source = candidate;
|
|
2321
|
+
const safe = {};
|
|
2322
|
+
for (const [key, value] of Object.entries(source)) {
|
|
2323
|
+
if (/present$/i.test(key) && typeof value === "boolean") {
|
|
2324
|
+
safe[key] = value;
|
|
2325
|
+
continue;
|
|
2326
|
+
}
|
|
2327
|
+
if (key === "retryable" && typeof value === "boolean") {
|
|
2328
|
+
safe.retryable = value;
|
|
2329
|
+
continue;
|
|
2330
|
+
}
|
|
2331
|
+
if (key === "code") {
|
|
2332
|
+
const sanitized = canonicalTopoloErrorCode2(value);
|
|
2333
|
+
if (sanitized) safe.code = sanitized;
|
|
2334
|
+
continue;
|
|
2335
|
+
}
|
|
2336
|
+
if (key === "source") {
|
|
2337
|
+
const sanitized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
2338
|
+
if (PUBLIC_DETAIL_SOURCES2.has(sanitized)) safe.source = sanitized;
|
|
2339
|
+
continue;
|
|
2340
|
+
}
|
|
2341
|
+
if (key === "dependency" || key === "platform") {
|
|
2342
|
+
safe[`${key}Present`] = value !== null && value !== void 0;
|
|
2343
|
+
continue;
|
|
2344
|
+
}
|
|
2345
|
+
if (key === "error" && !safe.code) {
|
|
2346
|
+
const sanitized = canonicalTopoloErrorCode2(value);
|
|
2347
|
+
if (sanitized) safe.code = sanitized;
|
|
2348
|
+
continue;
|
|
2349
|
+
}
|
|
2350
|
+
if (key === "required" && Array.isArray(value)) {
|
|
2351
|
+
const required = canonicalTopoloRequired2(value);
|
|
2352
|
+
if (required.length) safe.required = required;
|
|
2353
|
+
continue;
|
|
2354
|
+
}
|
|
2355
|
+
if (["missingFields", "fields", "paths"].includes(key) && Array.isArray(value)) {
|
|
2356
|
+
const paths = value.filter((item) => typeof item === "string" && /^[$.a-z0-9_[\]-]{1,160}$/i.test(item));
|
|
2357
|
+
if (paths.length) safe[key] = paths;
|
|
2358
|
+
}
|
|
4123
2359
|
}
|
|
4124
|
-
return
|
|
2360
|
+
return Object.keys(safe).length ? safe : void 0;
|
|
4125
2361
|
}
|
|
4126
2362
|
var TopoloHttpError2 = class extends TopoloSdkError2 {
|
|
4127
2363
|
status;
|
|
@@ -4130,15 +2366,19 @@ var TopoloHttpError2 = class extends TopoloSdkError2 {
|
|
|
4130
2366
|
path;
|
|
4131
2367
|
requestId;
|
|
4132
2368
|
retryAfter;
|
|
2369
|
+
details;
|
|
4133
2370
|
constructor(service, path, status, body, message, options = {}) {
|
|
4134
|
-
super(
|
|
2371
|
+
super(serviceErrorCode2(body, "http_error"), message ?? `HTTP ${status} from ${service}${path}`);
|
|
4135
2372
|
this.name = "TopoloHttpError";
|
|
4136
2373
|
this.status = status;
|
|
4137
2374
|
this.body = body;
|
|
4138
2375
|
this.service = service;
|
|
4139
2376
|
this.path = path;
|
|
4140
|
-
this.requestId = options.requestId;
|
|
4141
|
-
this.retryAfter = options.retryAfter;
|
|
2377
|
+
this.requestId = canonicalTopoloRequestId2(options.requestId);
|
|
2378
|
+
this.retryAfter = canonicalTopoloRetryAfter2(options.retryAfter);
|
|
2379
|
+
this.details = extractTopoloServiceErrorDetails2(
|
|
2380
|
+
options.details === void 0 ? body : { details: options.details }
|
|
2381
|
+
);
|
|
4142
2382
|
}
|
|
4143
2383
|
};
|
|
4144
2384
|
var APP_API_URLS2 = {
|
|
@@ -4244,14 +2484,14 @@ var TopoloOAuth = class {
|
|
|
4244
2484
|
return parsed;
|
|
4245
2485
|
}
|
|
4246
2486
|
};
|
|
4247
|
-
var
|
|
2487
|
+
var TOPOLO_ENVIRONMENT_SERVICE_URLS = {
|
|
4248
2488
|
production: {
|
|
2489
|
+
auth: "https://auth.topolo.app",
|
|
4249
2490
|
developers: "https://developers.topolo.app"
|
|
4250
2491
|
},
|
|
4251
2492
|
staging: {
|
|
4252
2493
|
auth: "https://auth.stg.topolo.us",
|
|
4253
|
-
developers: "https://developers.stg.topolo.us"
|
|
4254
|
-
seed: "https://seed.stg.topolo.us"
|
|
2494
|
+
developers: "https://developers.stg.topolo.us"
|
|
4255
2495
|
},
|
|
4256
2496
|
development: {
|
|
4257
2497
|
auth: "https://auth.topolo.dev",
|
|
@@ -4259,6 +2499,52 @@ var ENVIRONMENT_DEFAULT_SERVICE_URLS = {
|
|
|
4259
2499
|
},
|
|
4260
2500
|
local: {}
|
|
4261
2501
|
};
|
|
2502
|
+
function hostnameHasSuffix2(hostname, suffix) {
|
|
2503
|
+
return hostname === suffix || hostname.endsWith(`.${suffix}`);
|
|
2504
|
+
}
|
|
2505
|
+
function workersDevAccountMatches2(hostname, account) {
|
|
2506
|
+
if (!hostname.endsWith(".workers.dev")) return false;
|
|
2507
|
+
const labels = hostname.slice(0, -".workers.dev".length).split(".");
|
|
2508
|
+
return labels.length >= 2 && labels.at(-1) === account;
|
|
2509
|
+
}
|
|
2510
|
+
function originMatchesEnvironment2(url, environment) {
|
|
2511
|
+
const hostname = url.hostname.toLowerCase();
|
|
2512
|
+
if (environment === "local") {
|
|
2513
|
+
return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
2514
|
+
}
|
|
2515
|
+
if (url.protocol !== "https:") return false;
|
|
2516
|
+
if (environment === "production") {
|
|
2517
|
+
return hostnameHasSuffix2(hostname, "topolo.app") || hostnameHasSuffix2(hostname, "topolo.io") || workersDevAccountMatches2(hostname, "topolo");
|
|
2518
|
+
}
|
|
2519
|
+
if (environment === "staging") {
|
|
2520
|
+
return hostnameHasSuffix2(hostname, "stg.topolo.us") || workersDevAccountMatches2(hostname, "topolo-staging");
|
|
2521
|
+
}
|
|
2522
|
+
return hostnameHasSuffix2(hostname, "topolo.dev") || workersDevAccountMatches2(hostname, "topolo-development");
|
|
2523
|
+
}
|
|
2524
|
+
function assertTopoloServiceUrlEnvironment2(service, value, environment) {
|
|
2525
|
+
let url;
|
|
2526
|
+
try {
|
|
2527
|
+
url = new URL(value);
|
|
2528
|
+
} catch {
|
|
2529
|
+
throw new TopoloSdkError2("invalid_service_url", `Invalid URL configured for Topolo service "${service}".`);
|
|
2530
|
+
}
|
|
2531
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !originMatchesEnvironment2(url, environment)) {
|
|
2532
|
+
throw new TopoloSdkError2(
|
|
2533
|
+
"environment_mismatch",
|
|
2534
|
+
`Refusing Topolo service "${service}": ${url.origin} does not belong to ${environment}.`
|
|
2535
|
+
);
|
|
2536
|
+
}
|
|
2537
|
+
return url.origin;
|
|
2538
|
+
}
|
|
2539
|
+
function topoloServiceOrigins2(serviceUrls, environment) {
|
|
2540
|
+
return Object.fromEntries(
|
|
2541
|
+
Object.entries(serviceUrls ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(([service, value]) => [
|
|
2542
|
+
service,
|
|
2543
|
+
assertTopoloServiceUrlEnvironment2(service, value, environment)
|
|
2544
|
+
])
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
var ENVIRONMENT_DEFAULT_SERVICE_URLS = TOPOLO_ENVIRONMENT_SERVICE_URLS;
|
|
4262
2548
|
function configPath() {
|
|
4263
2549
|
const override = process.env.TOPOLO_CONFIG_PATH;
|
|
4264
2550
|
if (override) return override;
|
|
@@ -4339,9 +2625,9 @@ function writeProfileConfig(config, environment, profile) {
|
|
|
4339
2625
|
}
|
|
4340
2626
|
async function resolveCredential(environment = selectedEnvironment()) {
|
|
4341
2627
|
const prefix = envVarPrefix(environment);
|
|
4342
|
-
const apiKey = process.env[`${prefix}_API_KEY`] ?? process.env.TOPOLO_API_KEY;
|
|
2628
|
+
const apiKey = process.env[`${prefix}_API_KEY`] ?? (environment === "production" ? process.env.TOPOLO_API_KEY : void 0);
|
|
4343
2629
|
if (apiKey) return { kind: "api_key", apiKey };
|
|
4344
|
-
const accessToken = process.env[`${prefix}_ACCESS_TOKEN`] ?? process.env.TOPOLO_ACCESS_TOKEN;
|
|
2630
|
+
const accessToken = process.env[`${prefix}_ACCESS_TOKEN`] ?? (environment === "production" ? process.env.TOPOLO_ACCESS_TOKEN : void 0);
|
|
4345
2631
|
if (accessToken) return { kind: "access_token", accessToken };
|
|
4346
2632
|
const config = await readConfig();
|
|
4347
2633
|
return profileForEnvironment(config, environment).credential ?? null;
|
|
@@ -4349,7 +2635,7 @@ async function resolveCredential(environment = selectedEnvironment()) {
|
|
|
4349
2635
|
var REFRESH_SKEW_SECONDS = 60;
|
|
4350
2636
|
async function refreshCredentialIfNeeded(environment = selectedEnvironment(), options = {}) {
|
|
4351
2637
|
const prefix = envVarPrefix(environment);
|
|
4352
|
-
if (process.env[`${prefix}_API_KEY`] || process.env[`${prefix}_ACCESS_TOKEN`] || process.env.TOPOLO_API_KEY || process.env.TOPOLO_ACCESS_TOKEN) {
|
|
2638
|
+
if (process.env[`${prefix}_API_KEY`] || process.env[`${prefix}_ACCESS_TOKEN`] || environment === "production" && process.env.TOPOLO_API_KEY || environment === "production" && process.env.TOPOLO_ACCESS_TOKEN) {
|
|
4353
2639
|
return resolveCredential(environment);
|
|
4354
2640
|
}
|
|
4355
2641
|
const config = await readConfig();
|
|
@@ -4359,9 +2645,16 @@ async function refreshCredentialIfNeeded(environment = selectedEnvironment(), op
|
|
|
4359
2645
|
if (!cred.refreshToken || !profile.oauthClientId) return cred;
|
|
4360
2646
|
const now = Math.floor(Date.now() / 1e3);
|
|
4361
2647
|
if (!options.force && cred.expiresAt && cred.expiresAt - REFRESH_SKEW_SECONDS > now) return cred;
|
|
2648
|
+
const serviceUrls = resolveServiceUrls(profile.serviceUrls, environment);
|
|
2649
|
+
if (environment === "local" && !serviceUrls?.auth) {
|
|
2650
|
+
throw new TopoloSdkError2(
|
|
2651
|
+
"environment_service_url_required",
|
|
2652
|
+
"Local Topolo credential refresh requires an explicit local Auth service origin."
|
|
2653
|
+
);
|
|
2654
|
+
}
|
|
4362
2655
|
try {
|
|
4363
2656
|
const oauth = new TopoloOAuth({
|
|
4364
|
-
serviceUrls
|
|
2657
|
+
serviceUrls
|
|
4365
2658
|
});
|
|
4366
2659
|
const tokens = await oauth.refreshToken({
|
|
4367
2660
|
clientId: profile.oauthClientId,
|
|
@@ -4402,9 +2695,16 @@ async function refreshContextCredentialIfNeeded(environment, key, options = {})
|
|
|
4402
2695
|
if (!credential.refreshToken || !oauthClientId) return credential;
|
|
4403
2696
|
const now = Math.floor(Date.now() / 1e3);
|
|
4404
2697
|
if (!options.force && credential.expiresAt && credential.expiresAt - REFRESH_SKEW_SECONDS > now) return credential;
|
|
2698
|
+
const serviceUrls = resolveServiceUrls(profile.serviceUrls, environment);
|
|
2699
|
+
if (environment === "local" && !serviceUrls?.auth) {
|
|
2700
|
+
throw new TopoloSdkError2(
|
|
2701
|
+
"environment_service_url_required",
|
|
2702
|
+
"Local Topolo context refresh requires an explicit local Auth service origin."
|
|
2703
|
+
);
|
|
2704
|
+
}
|
|
4405
2705
|
try {
|
|
4406
2706
|
const oauth = new TopoloOAuth({
|
|
4407
|
-
serviceUrls
|
|
2707
|
+
serviceUrls
|
|
4408
2708
|
});
|
|
4409
2709
|
const tokens = await oauth.refreshToken({
|
|
4410
2710
|
clientId: oauthClientId,
|
|
@@ -4451,7 +2751,8 @@ function resolveServiceUrls(configUrls, environment = selectedEnvironment()) {
|
|
|
4451
2751
|
merged[k.slice("TOPOLO_SERVICE_URL_".length).toLowerCase()] = v;
|
|
4452
2752
|
}
|
|
4453
2753
|
}
|
|
4454
|
-
|
|
2754
|
+
const origins = topoloServiceOrigins2(merged, environment);
|
|
2755
|
+
return Object.keys(origins).length > 0 ? origins : void 0;
|
|
4455
2756
|
}
|
|
4456
2757
|
|
|
4457
2758
|
// src/server.ts
|
|
@@ -4462,11 +2763,7 @@ import {
|
|
|
4462
2763
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
4463
2764
|
|
|
4464
2765
|
// src/gating.ts
|
|
4465
|
-
function hasPlatformAccess(set) {
|
|
4466
|
-
return (set.role === "platform_super_admin" || set.role === "platform_admin") && set.orgSlug === "admin";
|
|
4467
|
-
}
|
|
4468
2766
|
function hasScope(set, required) {
|
|
4469
|
-
if (hasPlatformAccess(set)) return true;
|
|
4470
2767
|
if (set.permissions.includes("*")) return true;
|
|
4471
2768
|
const [servicePart, actionPart] = splitPermission(required);
|
|
4472
2769
|
if (!servicePart) return set.permissions.includes(required);
|
|
@@ -4816,6 +3113,38 @@ var TOOLS = [
|
|
|
4816
3113
|
return args["detail"] === true ? result : compactActionValidation(result.action.actionId, result.validation);
|
|
4817
3114
|
}
|
|
4818
3115
|
},
|
|
3116
|
+
{
|
|
3117
|
+
name: "topolo_prepare_action",
|
|
3118
|
+
title: "Prepare one Topolo action",
|
|
3119
|
+
description: "Fetches one exact live action contract once and returns one valid published example, missing fields, exact request, confirmation state, verification, and rollback without executing it.",
|
|
3120
|
+
requiredScopes: [],
|
|
3121
|
+
destructive: false,
|
|
3122
|
+
inputSchema: {
|
|
3123
|
+
type: "object",
|
|
3124
|
+
properties: {
|
|
3125
|
+
actionId: { type: "string", minLength: 1 },
|
|
3126
|
+
input: { type: "object", additionalProperties: true },
|
|
3127
|
+
resourceType: { type: "string" },
|
|
3128
|
+
resourceId: { type: "string" },
|
|
3129
|
+
confirm: { type: "boolean", description: "Include confirmation in the plan without executing the action." }
|
|
3130
|
+
},
|
|
3131
|
+
required: ["actionId"],
|
|
3132
|
+
additionalProperties: false
|
|
3133
|
+
},
|
|
3134
|
+
handler: async (topolo, args) => {
|
|
3135
|
+
const actionId = requiredString(args, "actionId");
|
|
3136
|
+
const resource = optionalResourceContext(args);
|
|
3137
|
+
const preparation = await topolo.client.prepareAction(
|
|
3138
|
+
actionId,
|
|
3139
|
+
args["input"] === void 0 ? void 0 : coerceObjectParam(args["input"], "input"),
|
|
3140
|
+
{
|
|
3141
|
+
confirm: args["confirm"] === true,
|
|
3142
|
+
...resource ? { resource } : {}
|
|
3143
|
+
}
|
|
3144
|
+
);
|
|
3145
|
+
return compactActionPreparation(preparation);
|
|
3146
|
+
}
|
|
3147
|
+
},
|
|
4819
3148
|
{
|
|
4820
3149
|
name: "topolo_plan_action",
|
|
4821
3150
|
title: "Plan one Topolo action",
|
|
@@ -5027,87 +3356,6 @@ var TOOLS = [
|
|
|
5027
3356
|
args["confirm"] === true
|
|
5028
3357
|
)
|
|
5029
3358
|
},
|
|
5030
|
-
{
|
|
5031
|
-
name: "topolo_list_application_requirements",
|
|
5032
|
-
title: "List Topolo application build requirements",
|
|
5033
|
-
description: "Returns the current versioned Topolo application-build contract, optionally filtered to one application. Use this before creating or expanding a Topolo app so the agent follows shared metadata, docs, auth, shell, service registration, deployment, observability, and verification requirements.",
|
|
5034
|
-
requiredScopes: [],
|
|
5035
|
-
destructive: false,
|
|
5036
|
-
inputSchema: {
|
|
5037
|
-
type: "object",
|
|
5038
|
-
properties: {
|
|
5039
|
-
application: {
|
|
5040
|
-
type: "string",
|
|
5041
|
-
description: "Optional application ID from topolo_search_applications."
|
|
5042
|
-
}
|
|
5043
|
-
},
|
|
5044
|
-
additionalProperties: false
|
|
5045
|
-
},
|
|
5046
|
-
handler: async (topolo, args) => {
|
|
5047
|
-
const application = args["application"];
|
|
5048
|
-
if (application === void 0) {
|
|
5049
|
-
return {
|
|
5050
|
-
version: APPLICATION_REQUIREMENTS_VERSION,
|
|
5051
|
-
application: null,
|
|
5052
|
-
scopes: ["all", "browser", "api", "tooling", "agent_surface"],
|
|
5053
|
-
requirements: APPLICATION_REQUIREMENTS
|
|
5054
|
-
};
|
|
5055
|
-
}
|
|
5056
|
-
const resolved = await resolveDirectoryApplication(topolo, application);
|
|
5057
|
-
const app = resolved.application;
|
|
5058
|
-
return {
|
|
5059
|
-
version: APPLICATION_REQUIREMENTS_VERSION,
|
|
5060
|
-
application: app,
|
|
5061
|
-
scopes: applicationRequirementScopes(app),
|
|
5062
|
-
requirements: requirementsForApplication(app)
|
|
5063
|
-
};
|
|
5064
|
-
}
|
|
5065
|
-
},
|
|
5066
|
-
{
|
|
5067
|
-
name: "topolo_audit_applications",
|
|
5068
|
-
title: "Audit Topolo applications against platform requirements",
|
|
5069
|
-
description: "Returns catalog-backed conformance scores and migration-queue items for all Topolo apps, or one application when provided. Use this to see which shared platform requirements need implementation, verification, or deeper review.",
|
|
5070
|
-
requiredScopes: [],
|
|
5071
|
-
destructive: false,
|
|
5072
|
-
inputSchema: {
|
|
5073
|
-
type: "object",
|
|
5074
|
-
properties: {
|
|
5075
|
-
application: {
|
|
5076
|
-
type: "string",
|
|
5077
|
-
description: "Optional application ID from topolo_search_applications."
|
|
5078
|
-
},
|
|
5079
|
-
failOn: {
|
|
5080
|
-
type: "string",
|
|
5081
|
-
enum: ["missing", "needs_review", "partial"],
|
|
5082
|
-
description: "Optional conformance gate. Returns conformanceGate.passed=false when findings at this severity or worse exist."
|
|
5083
|
-
},
|
|
5084
|
-
detail: {
|
|
5085
|
-
type: "boolean",
|
|
5086
|
-
description: "Include every finding and migration item. Defaults to per-app scores and a queue count."
|
|
5087
|
-
}
|
|
5088
|
-
},
|
|
5089
|
-
additionalProperties: false
|
|
5090
|
-
},
|
|
5091
|
-
handler: async (topolo, args) => {
|
|
5092
|
-
const application = args["application"];
|
|
5093
|
-
const failOn = args["failOn"];
|
|
5094
|
-
if (failOn !== void 0 && typeof failOn !== "string") {
|
|
5095
|
-
throw new TopoloMcpPublicError("failOn must be one of: missing, needs_review, partial");
|
|
5096
|
-
}
|
|
5097
|
-
const gate = failOn ? normalizeFailOn(failOn) : null;
|
|
5098
|
-
let report;
|
|
5099
|
-
if (application === void 0) {
|
|
5100
|
-
const directory = await listApplicationDirectory(topolo.client);
|
|
5101
|
-
report = auditApplicationEntries(directory.applications.map((app) => app.application));
|
|
5102
|
-
const output2 = args["detail"] === true ? report : compactApplicationAudit(report);
|
|
5103
|
-
return gate ? { ...output2, conformanceGate: evaluateApplicationAuditGate(report, gate) } : output2;
|
|
5104
|
-
}
|
|
5105
|
-
const resolved = await resolveDirectoryApplication(topolo, application);
|
|
5106
|
-
report = auditApplicationEntries([resolved.application]);
|
|
5107
|
-
const output = args["detail"] === true ? report : compactApplicationAudit(report);
|
|
5108
|
-
return gate ? { ...output, conformanceGate: evaluateApplicationAuditGate(report, gate) } : output;
|
|
5109
|
-
}
|
|
5110
|
-
},
|
|
5111
3359
|
{
|
|
5112
3360
|
name: "topolo_api_call",
|
|
5113
3361
|
title: "Call any Topolo platform service",
|
|
@@ -5207,57 +3455,9 @@ async function buildTopoloTools(topolo, scopes, tools = TOOLS) {
|
|
|
5207
3455
|
void topolo;
|
|
5208
3456
|
return filterToolsByScopes(tools, scopes);
|
|
5209
3457
|
}
|
|
5210
|
-
function evaluateApplicationAuditGate(report, threshold) {
|
|
5211
|
-
const counts = {
|
|
5212
|
-
met: 0,
|
|
5213
|
-
partial: 0,
|
|
5214
|
-
missing: 0,
|
|
5215
|
-
needs_review: 0
|
|
5216
|
-
};
|
|
5217
|
-
for (const audit of report.applications) {
|
|
5218
|
-
for (const finding2 of audit.findings) {
|
|
5219
|
-
counts[finding2.status] += 1;
|
|
5220
|
-
}
|
|
5221
|
-
}
|
|
5222
|
-
const failingFindings = statusesAtOrWorse(threshold).reduce(
|
|
5223
|
-
(total, status) => total + counts[status],
|
|
5224
|
-
0
|
|
5225
|
-
);
|
|
5226
|
-
return {
|
|
5227
|
-
threshold,
|
|
5228
|
-
passed: failingFindings === 0,
|
|
5229
|
-
message: failingFindings === 0 ? `No findings at or above ${threshold}.` : `${failingFindings} finding(s) at or above ${threshold}.`,
|
|
5230
|
-
failingFindings,
|
|
5231
|
-
counts
|
|
5232
|
-
};
|
|
5233
|
-
}
|
|
5234
|
-
function normalizeFailOn(value) {
|
|
5235
|
-
const normalized = value.trim().toLowerCase().replace(/-/g, "_");
|
|
5236
|
-
if (normalized === "missing" || normalized === "needs_review" || normalized === "partial") {
|
|
5237
|
-
return normalized;
|
|
5238
|
-
}
|
|
5239
|
-
throw new TopoloMcpPublicError("failOn must be one of: missing, needs_review, partial");
|
|
5240
|
-
}
|
|
5241
|
-
function statusesAtOrWorse(threshold) {
|
|
5242
|
-
if (threshold === "missing") return ["missing"];
|
|
5243
|
-
if (threshold === "needs_review") return ["missing", "needs_review"];
|
|
5244
|
-
return ["missing", "needs_review", "partial"];
|
|
5245
|
-
}
|
|
5246
3458
|
function displayAppId(entry) {
|
|
5247
3459
|
return friendlyAppId(entry);
|
|
5248
3460
|
}
|
|
5249
|
-
async function resolveDirectoryApplication(topolo, application) {
|
|
5250
|
-
if (typeof application !== "string" || !application.trim()) {
|
|
5251
|
-
throw new TopoloMcpPublicError("`application` must be a non-empty string.");
|
|
5252
|
-
}
|
|
5253
|
-
try {
|
|
5254
|
-
return await resolveApplicationDirectoryEntry(topolo.client, application);
|
|
5255
|
-
} catch (error) {
|
|
5256
|
-
throw new TopoloMcpPublicError(
|
|
5257
|
-
error instanceof Error ? error.message : `Unknown application "${application}".`
|
|
5258
|
-
);
|
|
5259
|
-
}
|
|
5260
|
-
}
|
|
5261
3461
|
|
|
5262
3462
|
// src/version.ts
|
|
5263
3463
|
import { readFileSync } from "fs";
|
|
@@ -5283,11 +3483,29 @@ function readMcpVersion() {
|
|
|
5283
3483
|
}
|
|
5284
3484
|
var MCP_VERSION = readMcpVersion();
|
|
5285
3485
|
|
|
3486
|
+
// src/context.ts
|
|
3487
|
+
function createMcpExecutionContext(input) {
|
|
3488
|
+
return createTopoloAgentExecutionContext(input);
|
|
3489
|
+
}
|
|
3490
|
+
function mcpServerInstructions(context) {
|
|
3491
|
+
return JSON.stringify({
|
|
3492
|
+
effectiveTopoloContext: context,
|
|
3493
|
+
rules: [
|
|
3494
|
+
"Use only this environment, identity, organization, context key, and service origins.",
|
|
3495
|
+
"Prepare mutations before execution and preserve the unchanged request for confirmation.",
|
|
3496
|
+
"Never infer undisclosed actions or switch organization context inside this MCP session."
|
|
3497
|
+
]
|
|
3498
|
+
});
|
|
3499
|
+
}
|
|
3500
|
+
function mcpResultEnvelope(context, data) {
|
|
3501
|
+
return { context, data };
|
|
3502
|
+
}
|
|
3503
|
+
|
|
5286
3504
|
// src/server.ts
|
|
5287
|
-
function buildServer(topolo, scopes, tools = TOOLS) {
|
|
3505
|
+
function buildServer(topolo, scopes, context, tools = TOOLS) {
|
|
5288
3506
|
const server = new Server(
|
|
5289
3507
|
{ name: "topolo", version: MCP_VERSION },
|
|
5290
|
-
{ capabilities: { tools: {} } }
|
|
3508
|
+
{ capabilities: { tools: {} }, instructions: mcpServerInstructions(context) }
|
|
5291
3509
|
);
|
|
5292
3510
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
5293
3511
|
const availableTools = await buildTopoloTools(topolo, scopes, tools);
|
|
@@ -5310,66 +3528,74 @@ function buildServer(topolo, scopes, tools = TOOLS) {
|
|
|
5310
3528
|
const tool = availableTools.find((t) => t.name === params.name);
|
|
5311
3529
|
if (!tool) {
|
|
5312
3530
|
return toolError(
|
|
3531
|
+
context,
|
|
5313
3532
|
`Tool "${params.name}" is not available for this credential. Either the tool does not exist or the current scopes do not permit it.`
|
|
5314
3533
|
);
|
|
5315
3534
|
}
|
|
5316
3535
|
if (!hasAnyScope(scopes, tool.requiredScopes)) {
|
|
5317
3536
|
return toolError(
|
|
3537
|
+
context,
|
|
5318
3538
|
`Tool "${tool.name}" requires one of: ${tool.requiredScopes.join(", ")}. Current credential does not grant any of these.`
|
|
5319
3539
|
);
|
|
5320
3540
|
}
|
|
5321
3541
|
try {
|
|
5322
3542
|
const result = await tool.handler(topolo, params.arguments ?? {});
|
|
5323
3543
|
return {
|
|
5324
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
3544
|
+
content: [{ type: "text", text: JSON.stringify(mcpResultEnvelope(context, result)) }]
|
|
5325
3545
|
};
|
|
5326
3546
|
} catch (err) {
|
|
5327
3547
|
if (err instanceof TopoloPermissionError) {
|
|
5328
|
-
return toolStructuredError(err,
|
|
3548
|
+
return toolStructuredError(context, err, err.message || "Permission denied.", {
|
|
5329
3549
|
required: err.required
|
|
5330
3550
|
});
|
|
5331
3551
|
}
|
|
5332
3552
|
if (err instanceof TopoloAuthError) {
|
|
5333
|
-
return toolStructuredError(err, "Authentication failed.");
|
|
3553
|
+
return toolStructuredError(context, err, "Authentication failed.");
|
|
5334
3554
|
}
|
|
5335
3555
|
if (err instanceof TopoloHttpError) {
|
|
5336
|
-
return toolStructuredError(err, err.message);
|
|
3556
|
+
return toolStructuredError(context, err, err.message);
|
|
5337
3557
|
}
|
|
5338
|
-
return toolError(publicMcpErrorMessage(err, "Tool execution failed."));
|
|
3558
|
+
return toolError(context, publicMcpErrorMessage(err, "Tool execution failed."));
|
|
5339
3559
|
}
|
|
5340
3560
|
});
|
|
5341
3561
|
return server;
|
|
5342
3562
|
}
|
|
5343
|
-
function toolStructuredError(error,
|
|
5344
|
-
return toolError(
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
}, null, 2));
|
|
3563
|
+
function toolStructuredError(context, error, _message, extra = {}) {
|
|
3564
|
+
return toolError(context, {
|
|
3565
|
+
code: error.code,
|
|
3566
|
+
status: error.status,
|
|
3567
|
+
message: publicTopoloErrorMessage(error),
|
|
3568
|
+
service: error.service,
|
|
3569
|
+
path: error.path,
|
|
3570
|
+
requestId: error.requestId,
|
|
3571
|
+
retryAfter: error.retryAfter,
|
|
3572
|
+
details: error.details,
|
|
3573
|
+
hint: topoloErrorHint(error),
|
|
3574
|
+
...extra
|
|
3575
|
+
});
|
|
5357
3576
|
}
|
|
5358
3577
|
async function advertisedToolNames(topolo, scopes, tools = TOOLS) {
|
|
5359
3578
|
return (await buildTopoloTools(topolo, scopes, tools)).map((t) => t.name);
|
|
5360
3579
|
}
|
|
5361
|
-
function toolError(
|
|
3580
|
+
function toolError(context, error) {
|
|
5362
3581
|
return {
|
|
5363
3582
|
isError: true,
|
|
5364
|
-
content: [{
|
|
3583
|
+
content: [{
|
|
3584
|
+
type: "text",
|
|
3585
|
+
text: JSON.stringify({
|
|
3586
|
+
context,
|
|
3587
|
+
error: typeof error === "string" ? { code: "mcp_error", message: error } : error
|
|
3588
|
+
})
|
|
3589
|
+
}]
|
|
5365
3590
|
};
|
|
5366
3591
|
}
|
|
5367
3592
|
|
|
5368
3593
|
// src/auth.ts
|
|
5369
|
-
function resolveCredentialFromEnv() {
|
|
5370
|
-
const
|
|
3594
|
+
function resolveCredentialFromEnv(environment = "production") {
|
|
3595
|
+
const prefix = environment === "production" ? "TOPOLO_PRODUCTION" : `TOPOLO_${environment.toUpperCase()}`;
|
|
3596
|
+
const apiKey = process.env[`${prefix}_API_KEY`] ?? (environment === "production" ? process.env.TOPOLO_API_KEY : void 0);
|
|
5371
3597
|
if (apiKey) return { kind: "api_key", apiKey };
|
|
5372
|
-
const accessToken = process.env.TOPOLO_ACCESS_TOKEN;
|
|
3598
|
+
const accessToken = process.env[`${prefix}_ACCESS_TOKEN`] ?? (environment === "production" ? process.env.TOPOLO_ACCESS_TOKEN : void 0);
|
|
5373
3599
|
if (accessToken) return { kind: "access_token", accessToken };
|
|
5374
3600
|
return null;
|
|
5375
3601
|
}
|
|
@@ -5401,7 +3627,7 @@ function resolvePinnedContextKey(profile, selector) {
|
|
|
5401
3627
|
throw new TopoloMcpPublicError(`TOPOLO_CONTEXT "${selector}" is not signed in for this environment`);
|
|
5402
3628
|
}
|
|
5403
3629
|
async function resolveMcpCredential(environment, selector = process.env.TOPOLO_CONTEXT, dependencies = DEFAULT_DEPENDENCIES) {
|
|
5404
|
-
const environmentCredential = dependencies.resolveCredentialFromEnv();
|
|
3630
|
+
const environmentCredential = dependencies.resolveCredentialFromEnv(environment);
|
|
5405
3631
|
const requestedSelector = selector?.trim();
|
|
5406
3632
|
if (environmentCredential) {
|
|
5407
3633
|
if (requestedSelector) {
|
|
@@ -5410,15 +3636,16 @@ async function resolveMcpCredential(environment, selector = process.env.TOPOLO_C
|
|
|
5410
3636
|
return { credential: environmentCredential, contextKey: null };
|
|
5411
3637
|
}
|
|
5412
3638
|
if (requestedSelector) {
|
|
5413
|
-
const
|
|
3639
|
+
const config2 = await dependencies.readConfig();
|
|
5414
3640
|
const contextKey = resolvePinnedContextKey(
|
|
5415
|
-
profileForEnvironment(
|
|
3641
|
+
profileForEnvironment(config2, environment),
|
|
5416
3642
|
requestedSelector
|
|
5417
3643
|
);
|
|
5418
3644
|
const credential = await dependencies.refreshContextCredentialIfNeeded(environment, contextKey);
|
|
5419
3645
|
return {
|
|
5420
3646
|
credential,
|
|
5421
3647
|
contextKey,
|
|
3648
|
+
storedContext: profileForEnvironment(config2, environment).contexts?.[contextKey],
|
|
5422
3649
|
credentialProvider: () => dependencies.refreshContextCredentialIfNeeded(
|
|
5423
3650
|
environment,
|
|
5424
3651
|
contextKey
|
|
@@ -5430,6 +3657,25 @@ async function resolveMcpCredential(environment, selector = process.env.TOPOLO_C
|
|
|
5430
3657
|
)
|
|
5431
3658
|
};
|
|
5432
3659
|
}
|
|
3660
|
+
const config = await dependencies.readConfig();
|
|
3661
|
+
const profile = profileForEnvironment(config, environment);
|
|
3662
|
+
const activeContext = profile.activeContext;
|
|
3663
|
+
if (activeContext) {
|
|
3664
|
+
const storedContext = profile.contexts?.[activeContext];
|
|
3665
|
+
if (!storedContext) {
|
|
3666
|
+
throw new TopoloMcpPublicError(`Active Topolo context "${activeContext}" is not signed in for this environment`);
|
|
3667
|
+
}
|
|
3668
|
+
return {
|
|
3669
|
+
credential: await dependencies.refreshContextCredentialIfNeeded(environment, activeContext),
|
|
3670
|
+
contextKey: activeContext,
|
|
3671
|
+
storedContext,
|
|
3672
|
+
credentialProvider: () => dependencies.refreshContextCredentialIfNeeded(environment, activeContext),
|
|
3673
|
+
refreshCredential: () => dependencies.refreshContextCredentialIfNeeded(environment, activeContext, { force: true })
|
|
3674
|
+
};
|
|
3675
|
+
}
|
|
3676
|
+
if (Object.keys(profile.contexts ?? {}).length > 0) {
|
|
3677
|
+
throw new TopoloMcpPublicError("No active Topolo context is selected for this environment");
|
|
3678
|
+
}
|
|
5433
3679
|
return {
|
|
5434
3680
|
credential: await dependencies.refreshCredentialIfNeeded(environment),
|
|
5435
3681
|
contextKey: null,
|
|
@@ -5484,6 +3730,7 @@ async function runStdioMcp(dependencies = DEFAULT_DEPENDENCIES2) {
|
|
|
5484
3730
|
const serviceUrls = dependencies.resolveServiceUrls(profile.serviceUrls, environment);
|
|
5485
3731
|
const topolo = dependencies.createTopolo({
|
|
5486
3732
|
credential,
|
|
3733
|
+
environment,
|
|
5487
3734
|
agent: {
|
|
5488
3735
|
clientName: "topolo-mcp",
|
|
5489
3736
|
clientVersion: MCP_VERSION,
|
|
@@ -5494,8 +3741,15 @@ async function runStdioMcp(dependencies = DEFAULT_DEPENDENCIES2) {
|
|
|
5494
3741
|
...resolvedCredential.credentialProvider ? { credentialProvider: resolvedCredential.credentialProvider } : {},
|
|
5495
3742
|
...resolvedCredential.refreshCredential ? { refreshCredential: resolvedCredential.refreshCredential } : {}
|
|
5496
3743
|
});
|
|
5497
|
-
const
|
|
5498
|
-
const
|
|
3744
|
+
const identity = await loadIdentity(topolo);
|
|
3745
|
+
const scopes = scopesForIdentity(identity);
|
|
3746
|
+
const executionContext = createMcpExecutionContext({
|
|
3747
|
+
environment,
|
|
3748
|
+
contextKey: resolvedCredential.contextKey,
|
|
3749
|
+
identity,
|
|
3750
|
+
serviceUrls
|
|
3751
|
+
});
|
|
3752
|
+
const server = dependencies.buildServer(topolo, scopes, executionContext);
|
|
5499
3753
|
const transport = dependencies.createTransport();
|
|
5500
3754
|
await server.connect(transport);
|
|
5501
3755
|
const toolNames = await dependencies.advertisedToolNames(topolo, scopes);
|
|
@@ -5505,14 +3759,19 @@ async function runStdioMcp(dependencies = DEFAULT_DEPENDENCIES2) {
|
|
|
5505
3759
|
);
|
|
5506
3760
|
}
|
|
5507
3761
|
async function loadScopes(topolo) {
|
|
3762
|
+
return scopesForIdentity(await loadIdentity(topolo));
|
|
3763
|
+
}
|
|
3764
|
+
function scopesForIdentity(info) {
|
|
3765
|
+
const userWithOrgSlug = info.user;
|
|
3766
|
+
return {
|
|
3767
|
+
permissions: info.user.permissions,
|
|
3768
|
+
role: info.user.role,
|
|
3769
|
+
orgSlug: info.organization?.slug || userWithOrgSlug.orgSlug || void 0
|
|
3770
|
+
};
|
|
3771
|
+
}
|
|
3772
|
+
async function loadIdentity(topolo) {
|
|
5508
3773
|
try {
|
|
5509
|
-
|
|
5510
|
-
const userWithOrgSlug = info.user;
|
|
5511
|
-
return {
|
|
5512
|
-
permissions: info.user.permissions,
|
|
5513
|
-
role: info.user.role,
|
|
5514
|
-
orgSlug: info.organization?.slug || userWithOrgSlug.orgSlug || void 0
|
|
5515
|
-
};
|
|
3774
|
+
return await topolo.identity.whoami();
|
|
5516
3775
|
} catch (err) {
|
|
5517
3776
|
if (err instanceof TopoloAuthError) {
|
|
5518
3777
|
throw new TopoloMcpPublicError("credential rejected.");
|
|
@@ -5544,9 +3803,11 @@ export {
|
|
|
5544
3803
|
createStdioTransport,
|
|
5545
3804
|
exitStdioProcess,
|
|
5546
3805
|
isDirectExecution,
|
|
3806
|
+
loadIdentity,
|
|
5547
3807
|
loadScopes,
|
|
5548
3808
|
readStdioEnvironment,
|
|
5549
3809
|
runStdioMcp,
|
|
3810
|
+
scopesForIdentity,
|
|
5550
3811
|
terminateStdioMcp,
|
|
5551
3812
|
writeStdioError
|
|
5552
3813
|
};
|