@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/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
- // ../topolo-sdk/dist/index.js
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("permission_denied", message);
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 extractHttpErrorCode(body) {
72
- if (!body || typeof body !== "object" || Array.isArray(body)) return "http_error";
166
+ function extractTopoloServiceErrorCode(body) {
167
+ if (!body || typeof body !== "object" || Array.isArray(body)) return null;
73
168
  const record = body;
74
- if (typeof record.code === "string" && record.code.trim()) return record.code.trim();
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
- if (typeof code === "string" && code.trim()) return code.trim();
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
- return "http_error";
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(extractHttpErrorCode(body), message ?? `HTTP ${status} from ${service}${path}`);
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
- return entry;
2188
- }
2189
- function requirementsForApplication(applicationOrId) {
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 buildAuditReport(applications) {
2216
- const migrationQueue = applications.flatMap(
2217
- (audit) => audit.findings.filter((finding2) => finding2.status !== "met").map((finding2) => ({
2218
- applicationId: audit.application.id,
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 evaluateRequirement(application, requirement) {
2240
- const evidence = [];
2241
- switch (requirement.id) {
2242
- case "platform-metadata": {
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 finding(requirement, status, reason, evidence) {
2342
- return {
2343
- requirementId: requirement.id,
2344
- title: requirement.title,
2345
- status,
2346
- reason,
2347
- evidence,
2348
- nextActions: status === "met" ? [] : requirement.implementation
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 scoreFindings(findings) {
2352
- const met = findings.filter((finding2) => finding2.status === "met").length;
2353
- const partial = findings.filter((finding2) => finding2.status === "partial").length;
2354
- const missing = findings.filter((finding2) => finding2.status === "missing").length;
2355
- const needsReview = findings.filter((finding2) => finding2.status === "needs_review").length;
2356
- const total = findings.length;
2357
- const weighted = met + partial * 0.5;
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
- function statusRank(status) {
2368
- if (status === "missing") return 0;
2369
- if (status === "partial") return 1;
2370
- return 2;
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.serviceUrls = options.serviceUrls;
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: requireHttpOrigin(opts.baseUrl) } : await this.resolveRequestService(opts.service);
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: err instanceof Error ? err.message : String(err)
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: message,
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") || requestId,
3010
- retryAfter: res.headers.get("Retry-After") || void 0
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(describeError(parsed, "Unauthorized"), "auth_error", errorContext);
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 { serviceKey, baseUrl: DEFAULT_APP_URLS[serviceKey] };
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: requireHttpOrigin(actionBaseUrl) } : null;
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.map(String);
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 compactApplicationAudit(report) {
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
- version: report.version,
3949
- applications: report.applications.map((audit) => ({
3950
- applicationId: audit.application.id,
3951
- applicationName: audit.application.name,
3952
- score: audit.score
3953
- })),
3954
- migrationQueueCount: report.migrationQueue.length
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
- // ../topolo-sdk/dist/node/credential-store.js
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 extractHttpErrorCode2(body) {
4116
- if (!body || typeof body !== "object" || Array.isArray(body)) return "http_error";
2291
+ function extractTopoloServiceErrorCode2(body) {
2292
+ if (!body || typeof body !== "object" || Array.isArray(body)) return null;
4117
2293
  const record = body;
4118
- if (typeof record.code === "string" && record.code.trim()) return record.code.trim();
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
- if (typeof code === "string" && code.trim()) return code.trim();
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 "http_error";
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(extractHttpErrorCode2(body), message ?? `HTTP ${status} from ${service}${path}`);
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 ENVIRONMENT_DEFAULT_SERVICE_URLS = {
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: resolveServiceUrls(profile.serviceUrls, environment)
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: resolveServiceUrls(profile.serviceUrls, environment)
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
- return Object.keys(merged).length > 0 ? merged : void 0;
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(result, null, 2) }]
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, `Permission denied. Required: ${err.required.join(", ") || "(unknown)"}`, {
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, message, extra = {}) {
5344
- return toolError(JSON.stringify({
5345
- error: {
5346
- code: error.code,
5347
- status: error.status,
5348
- message,
5349
- service: error.service,
5350
- path: error.path,
5351
- requestId: error.requestId,
5352
- retryAfter: error.retryAfter,
5353
- details: error.body,
5354
- ...extra
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(message) {
3580
+ function toolError(context, error) {
5362
3581
  return {
5363
3582
  isError: true,
5364
- content: [{ type: "text", text: message }]
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 apiKey = process.env.TOPOLO_API_KEY;
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 config = await dependencies.readConfig();
3639
+ const config2 = await dependencies.readConfig();
5414
3640
  const contextKey = resolvePinnedContextKey(
5415
- profileForEnvironment(config, environment),
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 scopes = await loadScopes(topolo);
5498
- const server = dependencies.buildServer(topolo, scopes);
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
- const info = await topolo.identity.whoami();
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
  };