@topolo/sdk 0.2.12 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +220 -73
- package/dist/index.d.cts +105 -42
- package/dist/index.d.ts +105 -42
- package/dist/index.js +220 -72
- package/package.json +5 -2
- package/src/application-requirements.ts +10 -6
- package/src/applications.generated.ts +61 -40
- package/src/client.test.ts +172 -6
- package/src/client.ts +214 -2
- package/src/index.ts +1 -1
- package/src/modules/p2p.test.ts +27 -11
- package/src/services.generated.ts +2 -24
- package/src/services.test.ts +5 -13
- package/src/services.ts +1 -2
- package/src/widgets.test.ts +7 -7
package/dist/index.js
CHANGED
|
@@ -91,41 +91,20 @@ var SERVICES = {
|
|
|
91
91
|
p2p: "https://p2p.topolo.app",
|
|
92
92
|
pay: "https://pay.topolo.app",
|
|
93
93
|
people: "https://people.topolo.app",
|
|
94
|
+
pulse: "https://pulse.topolo.app",
|
|
94
95
|
"quro.api": "https://api.ol0.me",
|
|
95
96
|
"quro.redirect": "https://ol0.me",
|
|
96
97
|
roadmapper: "https://roadmapper-api.topolo.app",
|
|
97
98
|
sign: "https://sign.topolo.app",
|
|
98
99
|
social_studio: "https://studio-api.topolo.app",
|
|
99
100
|
socialize: "https://socialize-api.topolo.app",
|
|
101
|
+
spaces: "https://spaces.topolo.app",
|
|
100
102
|
status: "https://status.topolo.app",
|
|
101
103
|
support: "https://support.topolo.app",
|
|
102
104
|
survey: "https://survey.topolo.app",
|
|
103
105
|
voice: "https://voice.topolo.app",
|
|
104
106
|
web: "https://web-api.topolo.app"
|
|
105
107
|
};
|
|
106
|
-
var PLATFORM_SERVICE_IDS = {
|
|
107
|
-
books: "srv_0g260hbN8oqv",
|
|
108
|
-
bugfix: "srv_kQ1t8qUSDgNs",
|
|
109
|
-
compose: "srv_aR3ETYmgygmd",
|
|
110
|
-
consent: "srv_8FIGR7Q7zP9r",
|
|
111
|
-
crm: "srv_iCwM4jGXcwlj",
|
|
112
|
-
customer_success: "srv_RWpOxkgZdZbH",
|
|
113
|
-
developers: "srv_NjDq9G9qyyjr",
|
|
114
|
-
director: "srv_wRCnLLbtvmVT",
|
|
115
|
-
flow: "srv_GBCuKkHhnRXv",
|
|
116
|
-
forms: "srv_UE7IcGe80J5o",
|
|
117
|
-
home: "srv_hE6HBldVzjth",
|
|
118
|
-
insights: "srv_DgbBMwWSIhmC",
|
|
119
|
-
inventory: "srv_hmvjbv3ZxDnh",
|
|
120
|
-
localize: "srv_Ze1g9OmXuZjF",
|
|
121
|
-
notify: "srv_9pRdkIjYyKX0",
|
|
122
|
-
p2p: "srv_4BzeLqH0wCve",
|
|
123
|
-
people: "srv_VDMBAonA5fKb",
|
|
124
|
-
sign: "srv_Bd8pRw5ZqRBT",
|
|
125
|
-
status: "srv_topolo_status",
|
|
126
|
-
survey: "srv_RCOi7CJkwf7R",
|
|
127
|
-
voice: "srv_BZANHkshGAK5"
|
|
128
|
-
};
|
|
129
108
|
|
|
130
109
|
// src/applications.generated.ts
|
|
131
110
|
var APPLICATIONS = {
|
|
@@ -205,6 +184,24 @@ var APPLICATIONS = {
|
|
|
205
184
|
}
|
|
206
185
|
]
|
|
207
186
|
},
|
|
187
|
+
"backup": {
|
|
188
|
+
"id": "backup",
|
|
189
|
+
"appDir": "TopoloBackup",
|
|
190
|
+
"name": "topolo-backup",
|
|
191
|
+
"packageName": "topolo-backup",
|
|
192
|
+
"productionUrl": null,
|
|
193
|
+
"services": [],
|
|
194
|
+
"deployTargets": [
|
|
195
|
+
{
|
|
196
|
+
"name": "topolo-backup",
|
|
197
|
+
"kind": "worker",
|
|
198
|
+
"apiUrl": null,
|
|
199
|
+
"domains": [],
|
|
200
|
+
"routes": [],
|
|
201
|
+
"serviceId": null
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
},
|
|
208
205
|
"books": {
|
|
209
206
|
"id": "books",
|
|
210
207
|
"appDir": "TopoloBooks",
|
|
@@ -355,15 +352,6 @@ var APPLICATIONS = {
|
|
|
355
352
|
}
|
|
356
353
|
]
|
|
357
354
|
},
|
|
358
|
-
"cli": {
|
|
359
|
-
"id": "cli",
|
|
360
|
-
"appDir": "TopoloCli",
|
|
361
|
-
"name": "@topolo/cli",
|
|
362
|
-
"packageName": "@topolo/cli",
|
|
363
|
-
"productionUrl": null,
|
|
364
|
-
"services": [],
|
|
365
|
-
"deployTargets": []
|
|
366
|
-
},
|
|
367
355
|
"cloudcontrol": {
|
|
368
356
|
"id": "cloudcontrol",
|
|
369
357
|
"appDir": "TopoloCloudControl",
|
|
@@ -529,24 +517,6 @@ var APPLICATIONS = {
|
|
|
529
517
|
}
|
|
530
518
|
]
|
|
531
519
|
},
|
|
532
|
-
"d1_backup": {
|
|
533
|
-
"id": "d1_backup",
|
|
534
|
-
"appDir": "TopoloD1Backup",
|
|
535
|
-
"name": "topolo-d1-backup",
|
|
536
|
-
"packageName": "topolo-d1-backup",
|
|
537
|
-
"productionUrl": null,
|
|
538
|
-
"services": [],
|
|
539
|
-
"deployTargets": [
|
|
540
|
-
{
|
|
541
|
-
"name": "topolo-d1-backup",
|
|
542
|
-
"kind": "worker",
|
|
543
|
-
"apiUrl": null,
|
|
544
|
-
"domains": [],
|
|
545
|
-
"routes": [],
|
|
546
|
-
"serviceId": null
|
|
547
|
-
}
|
|
548
|
-
]
|
|
549
|
-
},
|
|
550
520
|
"developers": {
|
|
551
521
|
"id": "developers",
|
|
552
522
|
"appDir": "TopoloDevelopers",
|
|
@@ -889,15 +859,6 @@ var APPLICATIONS = {
|
|
|
889
859
|
}
|
|
890
860
|
]
|
|
891
861
|
},
|
|
892
|
-
"mcp": {
|
|
893
|
-
"id": "mcp",
|
|
894
|
-
"appDir": "TopoloMCP",
|
|
895
|
-
"name": "@topolo/mcp",
|
|
896
|
-
"packageName": "@topolo/mcp",
|
|
897
|
-
"productionUrl": null,
|
|
898
|
-
"services": [],
|
|
899
|
-
"deployTargets": []
|
|
900
|
-
},
|
|
901
862
|
"mdm": {
|
|
902
863
|
"id": "mdm",
|
|
903
864
|
"appDir": "TopoloMDM",
|
|
@@ -1032,7 +993,7 @@ var APPLICATIONS = {
|
|
|
1032
993
|
"one": {
|
|
1033
994
|
"id": "one",
|
|
1034
995
|
"appDir": "TopoloOne",
|
|
1035
|
-
"name": "
|
|
996
|
+
"name": "TopoloOne",
|
|
1036
997
|
"packageName": "topolo-oneclick-sso",
|
|
1037
998
|
"productionUrl": "https://one.topolo.app",
|
|
1038
999
|
"services": [
|
|
@@ -1153,9 +1114,24 @@ var APPLICATIONS = {
|
|
|
1153
1114
|
"appDir": "TopoloPulse",
|
|
1154
1115
|
"name": "topolo-pulse",
|
|
1155
1116
|
"packageName": "topolo-pulse",
|
|
1156
|
-
"productionUrl":
|
|
1157
|
-
"services": [
|
|
1158
|
-
|
|
1117
|
+
"productionUrl": "https://pulse.topolo.app",
|
|
1118
|
+
"services": [
|
|
1119
|
+
"pulse"
|
|
1120
|
+
],
|
|
1121
|
+
"deployTargets": [
|
|
1122
|
+
{
|
|
1123
|
+
"name": "topolo-pulse",
|
|
1124
|
+
"kind": "worker",
|
|
1125
|
+
"apiUrl": "https://pulse.topolo.app",
|
|
1126
|
+
"domains": [
|
|
1127
|
+
"pulse.topolo.app"
|
|
1128
|
+
],
|
|
1129
|
+
"routes": [
|
|
1130
|
+
"pulse.topolo.app"
|
|
1131
|
+
],
|
|
1132
|
+
"serviceId": "pulse"
|
|
1133
|
+
}
|
|
1134
|
+
]
|
|
1159
1135
|
},
|
|
1160
1136
|
"quro": {
|
|
1161
1137
|
"id": "quro",
|
|
@@ -1338,6 +1314,30 @@ var APPLICATIONS = {
|
|
|
1338
1314
|
}
|
|
1339
1315
|
]
|
|
1340
1316
|
},
|
|
1317
|
+
"spaces": {
|
|
1318
|
+
"id": "spaces",
|
|
1319
|
+
"appDir": "TopoloSpaces",
|
|
1320
|
+
"name": "topolo-spaces",
|
|
1321
|
+
"packageName": "@topolo/spaces",
|
|
1322
|
+
"productionUrl": "https://spaces.topolo.app",
|
|
1323
|
+
"services": [
|
|
1324
|
+
"spaces"
|
|
1325
|
+
],
|
|
1326
|
+
"deployTargets": [
|
|
1327
|
+
{
|
|
1328
|
+
"name": "topolo-spaces",
|
|
1329
|
+
"kind": "worker",
|
|
1330
|
+
"apiUrl": "https://spaces.topolo.app",
|
|
1331
|
+
"domains": [
|
|
1332
|
+
"spaces.topolo.app"
|
|
1333
|
+
],
|
|
1334
|
+
"routes": [
|
|
1335
|
+
"spaces.topolo.app"
|
|
1336
|
+
],
|
|
1337
|
+
"serviceId": "spaces"
|
|
1338
|
+
}
|
|
1339
|
+
]
|
|
1340
|
+
},
|
|
1341
1341
|
"status": {
|
|
1342
1342
|
"id": "status",
|
|
1343
1343
|
"appDir": "TopoloStatus",
|
|
@@ -1519,12 +1519,12 @@ var APPLICATION_REQUIREMENTS = [
|
|
|
1519
1519
|
summary: "Applications must derive identity, organization, scopes, and service entitlement from TopoloAuth rather than inventing parallel auth state.",
|
|
1520
1520
|
appliesTo: ["browser", "api", "tooling", "agent_surface"],
|
|
1521
1521
|
evidence: [
|
|
1522
|
-
"@topolo/auth-client or @topolo/
|
|
1522
|
+
"@topolo/auth-client or @topolo-io/worker-runtime is used where applicable",
|
|
1523
1523
|
"No public command, tool, or SDK method accepts orgId",
|
|
1524
1524
|
"Service slug and Auth service registration are documented for protected services"
|
|
1525
1525
|
],
|
|
1526
1526
|
implementation: [
|
|
1527
|
-
"Use shared auth packages instead of copying token parsing or browser storage flows",
|
|
1527
|
+
"Use shared auth/runtime packages instead of copying token parsing, gateway, or browser storage flows",
|
|
1528
1528
|
"Derive organization from the credential on every request",
|
|
1529
1529
|
"Route OAuth, refresh, logout, and SSO paths according to the shared Auth contract"
|
|
1530
1530
|
]
|
|
@@ -1664,14 +1664,15 @@ var APPLICATION_REQUIREMENTS = [
|
|
|
1664
1664
|
];
|
|
1665
1665
|
function applicationRequirementScopes(application) {
|
|
1666
1666
|
const scopes = /* @__PURE__ */ new Set(["all"]);
|
|
1667
|
+
const appId = application.id;
|
|
1667
1668
|
const hasBrowserSurface = Boolean(application.productionUrl) || application.deployTargets.some((target) => target.kind === "pages");
|
|
1668
1669
|
const hasApiSurface = application.services.length > 0 || application.deployTargets.some((target) => Boolean(target.apiUrl));
|
|
1669
1670
|
if (hasBrowserSurface) scopes.add("browser");
|
|
1670
1671
|
if (hasApiSurface) scopes.add("api");
|
|
1671
|
-
if (
|
|
1672
|
+
if (appId === "cli" || appId === "mcp" || appId === "cloudcontrol") {
|
|
1672
1673
|
scopes.add("tooling");
|
|
1673
1674
|
}
|
|
1674
|
-
if (
|
|
1675
|
+
if (appId === "cli" || appId === "mcp" || hasApiSurface) {
|
|
1675
1676
|
scopes.add("agent_surface");
|
|
1676
1677
|
}
|
|
1677
1678
|
return Array.from(scopes);
|
|
@@ -1790,12 +1791,13 @@ function evaluateRequirement(application, requirement) {
|
|
|
1790
1791
|
return finding(requirement, "met", "CloudControl deploy target metadata is present.", evidence);
|
|
1791
1792
|
}
|
|
1792
1793
|
case "agent-and-operator-surfaces": {
|
|
1794
|
+
const appId = application.id;
|
|
1793
1795
|
if (application.services.length > 0) {
|
|
1794
1796
|
evidence.push(`services=${application.services.join(",")}`);
|
|
1795
1797
|
return finding(requirement, "partial", "SDK service discovery exists; typed SDK/CLI/MCP workflow coverage should be checked as APIs stabilize.", evidence);
|
|
1796
1798
|
}
|
|
1797
|
-
if (
|
|
1798
|
-
evidence.push(`tooling=${
|
|
1799
|
+
if (appId === "cli" || appId === "mcp" || appId === "cloudcontrol") {
|
|
1800
|
+
evidence.push(`tooling=${appId}`);
|
|
1799
1801
|
return finding(requirement, "met", "This application is itself an agent/operator surface.", evidence);
|
|
1800
1802
|
}
|
|
1801
1803
|
return finding(requirement, "missing", "No SDK service or tooling surface is registered.", evidence);
|
|
@@ -1870,20 +1872,38 @@ function resolveServiceUrl(service, overrides) {
|
|
|
1870
1872
|
|
|
1871
1873
|
// src/client.ts
|
|
1872
1874
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
1873
|
-
var
|
|
1875
|
+
var POSITIVE_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1876
|
+
var NEGATIVE_CACHE_TTL_MS = 60 * 1e3;
|
|
1877
|
+
var TopoloClient = class _TopoloClient {
|
|
1874
1878
|
credential;
|
|
1875
1879
|
agent;
|
|
1876
1880
|
serviceUrls;
|
|
1881
|
+
serviceIds;
|
|
1877
1882
|
requireConfirmForWrites;
|
|
1878
1883
|
timeoutMs;
|
|
1879
1884
|
fetchImpl;
|
|
1880
1885
|
debug;
|
|
1886
|
+
/**
|
|
1887
|
+
* Process-wide cache for slug → service-id lookups. Keyed by
|
|
1888
|
+
* `${authBaseUrl}|${slug}` so multiple `TopoloClient` instances pointing at
|
|
1889
|
+
* the same Auth share, but staging vs prod don't poison each other.
|
|
1890
|
+
* Cleared via `TopoloClient.clearServiceIdCache()` if a test needs it.
|
|
1891
|
+
*/
|
|
1892
|
+
static serviceIdCache = /* @__PURE__ */ new Map();
|
|
1893
|
+
/**
|
|
1894
|
+
* Drop the slug→service-id cache. Test-only — production callers should
|
|
1895
|
+
* let the 5-minute TTL expire naturally.
|
|
1896
|
+
*/
|
|
1897
|
+
static clearServiceIdCache() {
|
|
1898
|
+
_TopoloClient.serviceIdCache.clear();
|
|
1899
|
+
}
|
|
1881
1900
|
constructor(options) {
|
|
1882
1901
|
if (!options.credential) throw new TopoloAuthError("credential is required");
|
|
1883
1902
|
if (!options.agent?.clientName) throw new TopoloAuthError("agent.clientName is required");
|
|
1884
1903
|
this.credential = options.credential;
|
|
1885
1904
|
this.agent = options.agent;
|
|
1886
1905
|
this.serviceUrls = options.serviceUrls;
|
|
1906
|
+
this.serviceIds = options.serviceIds;
|
|
1887
1907
|
this.requireConfirmForWrites = options.requireConfirmForWrites !== false;
|
|
1888
1908
|
this.timeoutMs = options.timeoutMs ?? 3e4;
|
|
1889
1909
|
this.fetchImpl = options.fetch ?? fetch;
|
|
@@ -1918,7 +1938,7 @@ var TopoloClient = class {
|
|
|
1918
1938
|
const headers = new Headers();
|
|
1919
1939
|
headers.set("Accept", "application/json");
|
|
1920
1940
|
if (opts.body !== void 0) headers.set("Content-Type", "application/json");
|
|
1921
|
-
const platformServiceId =
|
|
1941
|
+
const platformServiceId = resolveConfiguredServiceId(opts.service, this.serviceIds);
|
|
1922
1942
|
if (platformServiceId) headers.set("X-Service-ID", platformServiceId);
|
|
1923
1943
|
applyAuthHeaders(headers, this.credential);
|
|
1924
1944
|
const requestId = generateRequestId();
|
|
@@ -1992,6 +2012,115 @@ var TopoloClient = class {
|
|
|
1992
2012
|
});
|
|
1993
2013
|
return parsed;
|
|
1994
2014
|
}
|
|
2015
|
+
/**
|
|
2016
|
+
* Resolve a service-slug → opaque service-id via caller config or TopoloAuth.
|
|
2017
|
+
*
|
|
2018
|
+
* Cache: 5 minutes per process per `(slug, auth_base_url)`. Negative
|
|
2019
|
+
* results (404) are cached for 60s so a typo doesn't hot-loop the
|
|
2020
|
+
* resolver. Returns null when the slug isn't registered — callers decide
|
|
2021
|
+
* whether to throw.
|
|
2022
|
+
*
|
|
2023
|
+
* No credential is sent: the endpoint is public (slugs aren't secret).
|
|
2024
|
+
* This keeps the resolver usable during early bootstrap before a caller
|
|
2025
|
+
* has acquired an access token.
|
|
2026
|
+
*/
|
|
2027
|
+
async getServiceIdBySlug(slug) {
|
|
2028
|
+
if (typeof slug !== "string") return null;
|
|
2029
|
+
const normalized = slug.trim().toLowerCase();
|
|
2030
|
+
if (!normalized) return null;
|
|
2031
|
+
const configuredServiceId = resolveConfiguredServiceId(normalized, this.serviceIds);
|
|
2032
|
+
if (configuredServiceId) return configuredServiceId;
|
|
2033
|
+
const authBase = resolveServiceUrl("auth", this.serviceUrls);
|
|
2034
|
+
const cacheKey = `${authBase}|${normalized}`;
|
|
2035
|
+
const now = Date.now();
|
|
2036
|
+
const cached = _TopoloClient.serviceIdCache.get(cacheKey);
|
|
2037
|
+
if (cached && cached.expiresAt > now) {
|
|
2038
|
+
return cached.value;
|
|
2039
|
+
}
|
|
2040
|
+
const url = new URL(
|
|
2041
|
+
`/api/services/by-slug/${encodeURIComponent(normalized)}`,
|
|
2042
|
+
authBase.endsWith("/") ? authBase : `${authBase}/`
|
|
2043
|
+
).toString();
|
|
2044
|
+
let res;
|
|
2045
|
+
try {
|
|
2046
|
+
res = await this.fetchImpl(url, {
|
|
2047
|
+
method: "GET",
|
|
2048
|
+
headers: { Accept: "application/json" }
|
|
2049
|
+
});
|
|
2050
|
+
} catch {
|
|
2051
|
+
return null;
|
|
2052
|
+
}
|
|
2053
|
+
if (res.status === 404) {
|
|
2054
|
+
_TopoloClient.serviceIdCache.set(cacheKey, {
|
|
2055
|
+
value: null,
|
|
2056
|
+
expiresAt: now + NEGATIVE_CACHE_TTL_MS
|
|
2057
|
+
});
|
|
2058
|
+
return null;
|
|
2059
|
+
}
|
|
2060
|
+
if (!res.ok) {
|
|
2061
|
+
return null;
|
|
2062
|
+
}
|
|
2063
|
+
const body = await res.json().catch(() => null);
|
|
2064
|
+
const serviceId = body?.data?.service_id;
|
|
2065
|
+
const value = typeof serviceId === "string" && serviceId ? serviceId : null;
|
|
2066
|
+
_TopoloClient.serviceIdCache.set(cacheKey, {
|
|
2067
|
+
value,
|
|
2068
|
+
expiresAt: now + POSITIVE_CACHE_TTL_MS
|
|
2069
|
+
});
|
|
2070
|
+
return value;
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* List the platform service catalog from the live registry
|
|
2074
|
+
* (`GET /api/services/catalog` on TopoloAuth). Credential-aware: the
|
|
2075
|
+
* caller's credential is attached, so an authenticated caller in an
|
|
2076
|
+
* entitled org also receives the `developer_only` services its role
|
|
2077
|
+
* permits; an unauthenticated/agent caller receives `active` only.
|
|
2078
|
+
*
|
|
2079
|
+
* Resilient: on any network/HTTP failure this falls back to the
|
|
2080
|
+
* build-time generated `APPLICATIONS` snapshot so offline/degraded
|
|
2081
|
+
* callers still get a usable (if static) catalog. The live result is
|
|
2082
|
+
* authoritative; the snapshot is a floor, not a cap.
|
|
2083
|
+
*/
|
|
2084
|
+
async listServices() {
|
|
2085
|
+
try {
|
|
2086
|
+
const res = await this.request({ service: "auth", method: "GET", path: "/api/services/catalog" });
|
|
2087
|
+
const raw = res.data?.services ?? res.services;
|
|
2088
|
+
if (Array.isArray(raw) && raw.length > 0) {
|
|
2089
|
+
const entries = [];
|
|
2090
|
+
for (const item of raw) {
|
|
2091
|
+
const s = item;
|
|
2092
|
+
const serviceId = typeof s.service_id === "string" ? s.service_id : typeof s.id === "string" ? s.id : null;
|
|
2093
|
+
if (!serviceId) continue;
|
|
2094
|
+
entries.push({
|
|
2095
|
+
serviceId,
|
|
2096
|
+
slug: typeof s.app_slug === "string" ? s.app_slug : null,
|
|
2097
|
+
name: typeof s.name === "string" ? s.name : null,
|
|
2098
|
+
description: typeof s.description === "string" ? s.description : null,
|
|
2099
|
+
baseUrl: typeof s.base_url === "string" ? s.base_url : null,
|
|
2100
|
+
status: typeof s.status === "string" ? s.status : null,
|
|
2101
|
+
surfaceKind: typeof s.surface_kind === "string" ? s.surface_kind : null,
|
|
2102
|
+
launchable: s.launchable === true
|
|
2103
|
+
});
|
|
2104
|
+
}
|
|
2105
|
+
if (entries.length > 0) return entries;
|
|
2106
|
+
}
|
|
2107
|
+
} catch {
|
|
2108
|
+
}
|
|
2109
|
+
return this.generatedCatalogFallback();
|
|
2110
|
+
}
|
|
2111
|
+
generatedCatalogFallback() {
|
|
2112
|
+
const apps = Object.values(APPLICATIONS);
|
|
2113
|
+
return apps.map((app) => ({
|
|
2114
|
+
serviceId: app.id,
|
|
2115
|
+
slug: app.id,
|
|
2116
|
+
name: app.name ?? app.id,
|
|
2117
|
+
description: null,
|
|
2118
|
+
baseUrl: app.productionUrl ?? null,
|
|
2119
|
+
status: "active",
|
|
2120
|
+
surfaceKind: "application",
|
|
2121
|
+
launchable: Boolean(app.productionUrl)
|
|
2122
|
+
}));
|
|
2123
|
+
}
|
|
1995
2124
|
/**
|
|
1996
2125
|
* Introspect the current credential. Returns the caller's resolved identity,
|
|
1997
2126
|
* organization, and permission set. Used by CLI `whoami` and by MCP to gate
|
|
@@ -2028,6 +2157,26 @@ var TopoloClient = class {
|
|
|
2028
2157
|
};
|
|
2029
2158
|
}
|
|
2030
2159
|
};
|
|
2160
|
+
function resolveConfiguredServiceId(slug, serviceIds) {
|
|
2161
|
+
for (const serviceKey of serviceKeyCandidates(slug)) {
|
|
2162
|
+
const configured = serviceIds?.[serviceKey];
|
|
2163
|
+
if (typeof configured === "string" && configured.trim()) return configured.trim();
|
|
2164
|
+
const envKey = `TOPOLO_SERVICE_ID_${serviceKey.replace(/\./g, "_").toUpperCase()}`;
|
|
2165
|
+
const envValue = typeof process !== "undefined" ? process.env?.[envKey] : void 0;
|
|
2166
|
+
if (typeof envValue === "string" && envValue.trim()) return envValue.trim();
|
|
2167
|
+
}
|
|
2168
|
+
return null;
|
|
2169
|
+
}
|
|
2170
|
+
function serviceKeyCandidates(slug) {
|
|
2171
|
+
const normalized = slug.trim().toLowerCase();
|
|
2172
|
+
const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
|
|
2173
|
+
return [.../* @__PURE__ */ new Set([
|
|
2174
|
+
normalized,
|
|
2175
|
+
normalized.replace(/-/g, "_"),
|
|
2176
|
+
withoutTopolo,
|
|
2177
|
+
withoutTopolo.replace(/-/g, "_")
|
|
2178
|
+
])];
|
|
2179
|
+
}
|
|
2031
2180
|
function describeError(body, fallback) {
|
|
2032
2181
|
if (body && typeof body === "object") {
|
|
2033
2182
|
const maybe = body;
|
|
@@ -2593,7 +2742,6 @@ export {
|
|
|
2593
2742
|
DEFAULT_SERVICE_URLS,
|
|
2594
2743
|
IdentityModule,
|
|
2595
2744
|
P2PModule,
|
|
2596
|
-
PLATFORM_SERVICE_IDS,
|
|
2597
2745
|
TOPOLO_WIDGET_ENDPOINT,
|
|
2598
2746
|
TOPOLO_WIDGET_REFRESH_INTERVAL_MS,
|
|
2599
2747
|
TopoloAuthError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Typed client SDK for the Topolo platform. Used by TopoloCli, TopoloMCP, and third-party agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,10 +25,13 @@
|
|
|
25
25
|
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
|
|
26
26
|
"typecheck": "node scripts/generate-services.mjs && tsc --noEmit",
|
|
27
27
|
"test": "vitest run",
|
|
28
|
-
"test:watch": "vitest"
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"prepublishOnly": "npm run build",
|
|
30
|
+
"coverage": "vitest run --coverage"
|
|
29
31
|
},
|
|
30
32
|
"devDependencies": {
|
|
31
33
|
"@types/node": "^20.19.39",
|
|
34
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
32
35
|
"tsup": "^8.0.0",
|
|
33
36
|
"typescript": "^5.4.0",
|
|
34
37
|
"vitest": "^2.1.0"
|
|
@@ -108,12 +108,12 @@ export const APPLICATION_REQUIREMENTS = [
|
|
|
108
108
|
'Applications must derive identity, organization, scopes, and service entitlement from TopoloAuth rather than inventing parallel auth state.',
|
|
109
109
|
appliesTo: ['browser', 'api', 'tooling', 'agent_surface'],
|
|
110
110
|
evidence: [
|
|
111
|
-
'@topolo/auth-client or @topolo/
|
|
111
|
+
'@topolo/auth-client or @topolo-io/worker-runtime is used where applicable',
|
|
112
112
|
'No public command, tool, or SDK method accepts orgId',
|
|
113
113
|
'Service slug and Auth service registration are documented for protected services',
|
|
114
114
|
],
|
|
115
115
|
implementation: [
|
|
116
|
-
'Use shared auth packages instead of copying token parsing or browser storage flows',
|
|
116
|
+
'Use shared auth/runtime packages instead of copying token parsing, gateway, or browser storage flows',
|
|
117
117
|
'Derive organization from the credential on every request',
|
|
118
118
|
'Route OAuth, refresh, logout, and SSO paths according to the shared Auth contract',
|
|
119
119
|
],
|
|
@@ -264,6 +264,9 @@ export function applicationRequirementScopes(
|
|
|
264
264
|
application: ApplicationCatalogEntry,
|
|
265
265
|
): ApplicationRequirementScope[] {
|
|
266
266
|
const scopes = new Set<ApplicationRequirementScope>(['all']);
|
|
267
|
+
// id is checked by value: tooling ids (cli/mcp) are intentionally not in
|
|
268
|
+
// the deploy catalog union, so compare as string rather than assert overlap.
|
|
269
|
+
const appId: string = application.id;
|
|
267
270
|
const hasBrowserSurface =
|
|
268
271
|
Boolean(application.productionUrl) || application.deployTargets.some((target) => target.kind === 'pages');
|
|
269
272
|
const hasApiSurface =
|
|
@@ -272,10 +275,10 @@ export function applicationRequirementScopes(
|
|
|
272
275
|
|
|
273
276
|
if (hasBrowserSurface) scopes.add('browser');
|
|
274
277
|
if (hasApiSurface) scopes.add('api');
|
|
275
|
-
if (
|
|
278
|
+
if (appId === 'cli' || appId === 'mcp' || appId === 'cloudcontrol') {
|
|
276
279
|
scopes.add('tooling');
|
|
277
280
|
}
|
|
278
|
-
if (
|
|
281
|
+
if (appId === 'cli' || appId === 'mcp' || hasApiSurface) {
|
|
279
282
|
scopes.add('agent_surface');
|
|
280
283
|
}
|
|
281
284
|
|
|
@@ -420,12 +423,13 @@ function evaluateRequirement(
|
|
|
420
423
|
return finding(requirement, 'met', 'CloudControl deploy target metadata is present.', evidence);
|
|
421
424
|
}
|
|
422
425
|
case 'agent-and-operator-surfaces': {
|
|
426
|
+
const appId: string = application.id;
|
|
423
427
|
if (application.services.length > 0) {
|
|
424
428
|
evidence.push(`services=${application.services.join(',')}`);
|
|
425
429
|
return finding(requirement, 'partial', 'SDK service discovery exists; typed SDK/CLI/MCP workflow coverage should be checked as APIs stabilize.', evidence);
|
|
426
430
|
}
|
|
427
|
-
if (
|
|
428
|
-
evidence.push(`tooling=${
|
|
431
|
+
if (appId === 'cli' || appId === 'mcp' || appId === 'cloudcontrol') {
|
|
432
|
+
evidence.push(`tooling=${appId}`);
|
|
429
433
|
return finding(requirement, 'met', 'This application is itself an agent/operator surface.', evidence);
|
|
430
434
|
}
|
|
431
435
|
return finding(requirement, 'missing', 'No SDK service or tooling surface is registered.', evidence);
|
|
@@ -79,6 +79,24 @@ export const APPLICATIONS = {
|
|
|
79
79
|
}
|
|
80
80
|
]
|
|
81
81
|
},
|
|
82
|
+
"backup": {
|
|
83
|
+
"id": "backup",
|
|
84
|
+
"appDir": "TopoloBackup",
|
|
85
|
+
"name": "topolo-backup",
|
|
86
|
+
"packageName": "topolo-backup",
|
|
87
|
+
"productionUrl": null,
|
|
88
|
+
"services": [],
|
|
89
|
+
"deployTargets": [
|
|
90
|
+
{
|
|
91
|
+
"name": "topolo-backup",
|
|
92
|
+
"kind": "worker",
|
|
93
|
+
"apiUrl": null,
|
|
94
|
+
"domains": [],
|
|
95
|
+
"routes": [],
|
|
96
|
+
"serviceId": null
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
},
|
|
82
100
|
"books": {
|
|
83
101
|
"id": "books",
|
|
84
102
|
"appDir": "TopoloBooks",
|
|
@@ -229,15 +247,6 @@ export const APPLICATIONS = {
|
|
|
229
247
|
}
|
|
230
248
|
]
|
|
231
249
|
},
|
|
232
|
-
"cli": {
|
|
233
|
-
"id": "cli",
|
|
234
|
-
"appDir": "TopoloCli",
|
|
235
|
-
"name": "@topolo/cli",
|
|
236
|
-
"packageName": "@topolo/cli",
|
|
237
|
-
"productionUrl": null,
|
|
238
|
-
"services": [],
|
|
239
|
-
"deployTargets": []
|
|
240
|
-
},
|
|
241
250
|
"cloudcontrol": {
|
|
242
251
|
"id": "cloudcontrol",
|
|
243
252
|
"appDir": "TopoloCloudControl",
|
|
@@ -403,24 +412,6 @@ export const APPLICATIONS = {
|
|
|
403
412
|
}
|
|
404
413
|
]
|
|
405
414
|
},
|
|
406
|
-
"d1_backup": {
|
|
407
|
-
"id": "d1_backup",
|
|
408
|
-
"appDir": "TopoloD1Backup",
|
|
409
|
-
"name": "topolo-d1-backup",
|
|
410
|
-
"packageName": "topolo-d1-backup",
|
|
411
|
-
"productionUrl": null,
|
|
412
|
-
"services": [],
|
|
413
|
-
"deployTargets": [
|
|
414
|
-
{
|
|
415
|
-
"name": "topolo-d1-backup",
|
|
416
|
-
"kind": "worker",
|
|
417
|
-
"apiUrl": null,
|
|
418
|
-
"domains": [],
|
|
419
|
-
"routes": [],
|
|
420
|
-
"serviceId": null
|
|
421
|
-
}
|
|
422
|
-
]
|
|
423
|
-
},
|
|
424
415
|
"developers": {
|
|
425
416
|
"id": "developers",
|
|
426
417
|
"appDir": "TopoloDevelopers",
|
|
@@ -763,15 +754,6 @@ export const APPLICATIONS = {
|
|
|
763
754
|
}
|
|
764
755
|
]
|
|
765
756
|
},
|
|
766
|
-
"mcp": {
|
|
767
|
-
"id": "mcp",
|
|
768
|
-
"appDir": "TopoloMCP",
|
|
769
|
-
"name": "@topolo/mcp",
|
|
770
|
-
"packageName": "@topolo/mcp",
|
|
771
|
-
"productionUrl": null,
|
|
772
|
-
"services": [],
|
|
773
|
-
"deployTargets": []
|
|
774
|
-
},
|
|
775
757
|
"mdm": {
|
|
776
758
|
"id": "mdm",
|
|
777
759
|
"appDir": "TopoloMDM",
|
|
@@ -906,7 +888,7 @@ export const APPLICATIONS = {
|
|
|
906
888
|
"one": {
|
|
907
889
|
"id": "one",
|
|
908
890
|
"appDir": "TopoloOne",
|
|
909
|
-
"name": "
|
|
891
|
+
"name": "TopoloOne",
|
|
910
892
|
"packageName": "topolo-oneclick-sso",
|
|
911
893
|
"productionUrl": "https://one.topolo.app",
|
|
912
894
|
"services": [
|
|
@@ -1027,9 +1009,24 @@ export const APPLICATIONS = {
|
|
|
1027
1009
|
"appDir": "TopoloPulse",
|
|
1028
1010
|
"name": "topolo-pulse",
|
|
1029
1011
|
"packageName": "topolo-pulse",
|
|
1030
|
-
"productionUrl":
|
|
1031
|
-
"services": [
|
|
1032
|
-
|
|
1012
|
+
"productionUrl": "https://pulse.topolo.app",
|
|
1013
|
+
"services": [
|
|
1014
|
+
"pulse"
|
|
1015
|
+
],
|
|
1016
|
+
"deployTargets": [
|
|
1017
|
+
{
|
|
1018
|
+
"name": "topolo-pulse",
|
|
1019
|
+
"kind": "worker",
|
|
1020
|
+
"apiUrl": "https://pulse.topolo.app",
|
|
1021
|
+
"domains": [
|
|
1022
|
+
"pulse.topolo.app"
|
|
1023
|
+
],
|
|
1024
|
+
"routes": [
|
|
1025
|
+
"pulse.topolo.app"
|
|
1026
|
+
],
|
|
1027
|
+
"serviceId": "pulse"
|
|
1028
|
+
}
|
|
1029
|
+
]
|
|
1033
1030
|
},
|
|
1034
1031
|
"quro": {
|
|
1035
1032
|
"id": "quro",
|
|
@@ -1212,6 +1209,30 @@ export const APPLICATIONS = {
|
|
|
1212
1209
|
}
|
|
1213
1210
|
]
|
|
1214
1211
|
},
|
|
1212
|
+
"spaces": {
|
|
1213
|
+
"id": "spaces",
|
|
1214
|
+
"appDir": "TopoloSpaces",
|
|
1215
|
+
"name": "topolo-spaces",
|
|
1216
|
+
"packageName": "@topolo/spaces",
|
|
1217
|
+
"productionUrl": "https://spaces.topolo.app",
|
|
1218
|
+
"services": [
|
|
1219
|
+
"spaces"
|
|
1220
|
+
],
|
|
1221
|
+
"deployTargets": [
|
|
1222
|
+
{
|
|
1223
|
+
"name": "topolo-spaces",
|
|
1224
|
+
"kind": "worker",
|
|
1225
|
+
"apiUrl": "https://spaces.topolo.app",
|
|
1226
|
+
"domains": [
|
|
1227
|
+
"spaces.topolo.app"
|
|
1228
|
+
],
|
|
1229
|
+
"routes": [
|
|
1230
|
+
"spaces.topolo.app"
|
|
1231
|
+
],
|
|
1232
|
+
"serviceId": "spaces"
|
|
1233
|
+
}
|
|
1234
|
+
]
|
|
1235
|
+
},
|
|
1215
1236
|
"status": {
|
|
1216
1237
|
"id": "status",
|
|
1217
1238
|
"appDir": "TopoloStatus",
|