@topolo/cli 0.13.25 → 0.13.27
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 +795 -183
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -308,7 +308,7 @@ import { Command, Help } from "commander";
|
|
|
308
308
|
import { createInterface } from "readline/promises";
|
|
309
309
|
import { stdin as input } from "process";
|
|
310
310
|
|
|
311
|
-
// node_modules
|
|
311
|
+
// node_modules/@topolo/sdk/dist/index.js
|
|
312
312
|
function record(value) {
|
|
313
313
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
314
314
|
}
|
|
@@ -326,74 +326,6 @@ function validTopoloActionAccessPolicy(value) {
|
|
|
326
326
|
}
|
|
327
327
|
return true;
|
|
328
328
|
}
|
|
329
|
-
function record2(value) {
|
|
330
|
-
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
331
|
-
}
|
|
332
|
-
function text(value, limit = 512) {
|
|
333
|
-
return typeof value === "string" && value.trim() ? value.trim().slice(0, limit) : null;
|
|
334
|
-
}
|
|
335
|
-
function strings2(value) {
|
|
336
|
-
return Array.isArray(value) ? value.slice(0, 8).flatMap((item) => {
|
|
337
|
-
const result = text(item);
|
|
338
|
-
return result ? [result] : [];
|
|
339
|
-
}) : [];
|
|
340
|
-
}
|
|
341
|
-
function pairs(value, first, second) {
|
|
342
|
-
return Array.isArray(value) ? value.slice(0, 8).flatMap((item) => {
|
|
343
|
-
if (!record2(item)) return [];
|
|
344
|
-
const a = text(item[first], 160);
|
|
345
|
-
const b = text(item[second]);
|
|
346
|
-
return a && b ? [{ [first]: a, [second]: b }] : [];
|
|
347
|
-
}) : [];
|
|
348
|
-
}
|
|
349
|
-
function docs(value) {
|
|
350
|
-
const result = text(value, 2048);
|
|
351
|
-
if (!result) return null;
|
|
352
|
-
try {
|
|
353
|
-
const url = new URL(result);
|
|
354
|
-
return url.protocol === "https:" && !url.username && !url.password ? url.href : null;
|
|
355
|
-
} catch {
|
|
356
|
-
return null;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
function normalizeApplicationGuide(value) {
|
|
360
|
-
if (!record2(value) || value.schemaVersion !== 1 || value.source !== "published_application_manifest") return null;
|
|
361
|
-
const purpose = text(value.purpose, 2048);
|
|
362
|
-
const gettingStarted = strings2(value.gettingStarted);
|
|
363
|
-
const docsUrl = docs(value.docsUrl);
|
|
364
|
-
return {
|
|
365
|
-
schemaVersion: 1,
|
|
366
|
-
source: "published_application_manifest",
|
|
367
|
-
publishedAt: text(value.publishedAt, 160),
|
|
368
|
-
purpose,
|
|
369
|
-
problemStatement: text(value.problemStatement, 2048),
|
|
370
|
-
solutionStatement: text(value.solutionStatement, 2048),
|
|
371
|
-
benefits: strings2(value.benefits),
|
|
372
|
-
features: pairs(value.features, "title", "description"),
|
|
373
|
-
gettingStarted,
|
|
374
|
-
worksWellWith: pairs(value.worksWellWith, "appId", "reason"),
|
|
375
|
-
docsUrl,
|
|
376
|
-
missingFields: [...!purpose ? ["purpose"] : [], ...!docsUrl ? ["docsUrl"] : [], ...!gettingStarted.length ? ["gettingStarted"] : []]
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
function applyAuthHeaders(headers, credential) {
|
|
380
|
-
if (credential.kind === "api_key") {
|
|
381
|
-
headers.set("X-Api-Key", credential.apiKey);
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
headers.set("Authorization", `${credential.kind === "dpop_access_token" ? "DPoP" : "Bearer"} ${credential.accessToken}`);
|
|
385
|
-
}
|
|
386
|
-
function applyAuditHeaders(headers, agent, requestId) {
|
|
387
|
-
headers.set("X-Topolo-Client", `${agent.clientName}/${agent.clientVersion}`);
|
|
388
|
-
if (agent.agentName) headers.set("X-Topolo-Agent", agent.agentName);
|
|
389
|
-
headers.set("X-Topolo-Request-Id", requestId);
|
|
390
|
-
}
|
|
391
|
-
function generateRequestId() {
|
|
392
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
393
|
-
return crypto.randomUUID();
|
|
394
|
-
}
|
|
395
|
-
return `req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
396
|
-
}
|
|
397
329
|
var TopoloSdkError = class extends Error {
|
|
398
330
|
code;
|
|
399
331
|
constructor(code, message3) {
|
|
@@ -823,6 +755,424 @@ var TopoloReauthRequiredError = class extends TopoloAuthError {
|
|
|
823
755
|
this.reauthorization = Object.freeze({ ...reauthorization });
|
|
824
756
|
}
|
|
825
757
|
};
|
|
758
|
+
var TOPOLO_FIRST_PARTY_ORIGINS = Object.freeze({
|
|
759
|
+
production: Object.freeze([
|
|
760
|
+
"https://admin.topolo.app",
|
|
761
|
+
"https://agent.topolo.app",
|
|
762
|
+
"https://api.ol0.me",
|
|
763
|
+
"https://auth.topolo.app",
|
|
764
|
+
"https://backup.topolo.app",
|
|
765
|
+
"https://blog.topolo.app",
|
|
766
|
+
"https://books.topolo.app",
|
|
767
|
+
"https://brand.topolo.app",
|
|
768
|
+
"https://bugfix.topolo.app",
|
|
769
|
+
"https://bytes.topolo.app",
|
|
770
|
+
"https://calendar.topolo.app",
|
|
771
|
+
"https://campaigns.topolo.app",
|
|
772
|
+
"https://capacity.topolo.app",
|
|
773
|
+
"https://chat.topolo.app",
|
|
774
|
+
"https://commerce.topolo.app",
|
|
775
|
+
"https://compose.topolo.app",
|
|
776
|
+
"https://consent.topolo.app",
|
|
777
|
+
"https://contracts.topolo.app",
|
|
778
|
+
"https://crm.topolo.app",
|
|
779
|
+
"https://design.topolo.app",
|
|
780
|
+
"https://developers.topolo.app",
|
|
781
|
+
"https://director.topolo.app",
|
|
782
|
+
"https://docs.topolo.app",
|
|
783
|
+
"https://engage.topolo.app",
|
|
784
|
+
"https://events.topolo.app",
|
|
785
|
+
"https://feed-analytics-api.topolo.app",
|
|
786
|
+
"https://feed-api.topolo.app",
|
|
787
|
+
"https://flow.topolo.app",
|
|
788
|
+
"https://forecast.topolo.app",
|
|
789
|
+
"https://forms.topolo.app",
|
|
790
|
+
"https://home.topolo.app",
|
|
791
|
+
"https://insights.topolo.app",
|
|
792
|
+
"https://inventory.topolo.app",
|
|
793
|
+
"https://learn.topolo.app",
|
|
794
|
+
"https://localize.topolo.app",
|
|
795
|
+
"https://mail.topolo.app",
|
|
796
|
+
"https://mcp.topolo.app",
|
|
797
|
+
"https://mdm.topolo.app",
|
|
798
|
+
"https://messages.topolo.app",
|
|
799
|
+
"https://nexus.topolo.app",
|
|
800
|
+
"https://notify.topolo.app",
|
|
801
|
+
"https://nurture.topolo.app",
|
|
802
|
+
"https://observability-watch.topolo.app",
|
|
803
|
+
"https://p2p.topolo.app",
|
|
804
|
+
"https://pay.topolo.app",
|
|
805
|
+
"https://people.topolo.app",
|
|
806
|
+
"https://quro.topolo.app",
|
|
807
|
+
"https://recruit.topolo.app",
|
|
808
|
+
"https://render.topolo.app",
|
|
809
|
+
"https://roadmap.topolo.app",
|
|
810
|
+
"https://sign.topolo.app",
|
|
811
|
+
"https://socialize-api.topolo.app",
|
|
812
|
+
"https://spaces.topolo.app",
|
|
813
|
+
"https://speech.topolo.app",
|
|
814
|
+
"https://status.topolo.app",
|
|
815
|
+
"https://studio.topolo.app",
|
|
816
|
+
"https://success.topolo.app",
|
|
817
|
+
"https://support.topolo.app",
|
|
818
|
+
"https://survey.topolo.app",
|
|
819
|
+
"https://voice.topolo.app",
|
|
820
|
+
"https://web.topolo.app",
|
|
821
|
+
"https://www.topolo.app"
|
|
822
|
+
]),
|
|
823
|
+
staging: Object.freeze([
|
|
824
|
+
"https://admin.stg.topolo.us",
|
|
825
|
+
"https://agent.stg.topolo.us",
|
|
826
|
+
"https://api.one.stg.topolo.us",
|
|
827
|
+
"https://auth.stg.topolo.us",
|
|
828
|
+
"https://backup.stg.topolo.us",
|
|
829
|
+
"https://blog.stg.topolo.us",
|
|
830
|
+
"https://books.stg.topolo.us",
|
|
831
|
+
"https://brand.stg.topolo.us",
|
|
832
|
+
"https://bugfix.stg.topolo.us",
|
|
833
|
+
"https://bytes.stg.topolo.us",
|
|
834
|
+
"https://calendar.stg.topolo.us",
|
|
835
|
+
"https://campaigns.stg.topolo.us",
|
|
836
|
+
"https://capacity.stg.topolo.us",
|
|
837
|
+
"https://chat.stg.topolo.us",
|
|
838
|
+
"https://commerce.stg.topolo.us",
|
|
839
|
+
"https://compose.stg.topolo.us",
|
|
840
|
+
"https://consent.stg.topolo.us",
|
|
841
|
+
"https://contracts.stg.topolo.us",
|
|
842
|
+
"https://crm.stg.topolo.us",
|
|
843
|
+
"https://design.stg.topolo.us",
|
|
844
|
+
"https://developers.stg.topolo.us",
|
|
845
|
+
"https://director.stg.topolo.us",
|
|
846
|
+
"https://docs.stg.topolo.us",
|
|
847
|
+
"https://engage.stg.topolo.us",
|
|
848
|
+
"https://events.stg.topolo.us",
|
|
849
|
+
"https://feed-analytics-api.stg.topolo.us",
|
|
850
|
+
"https://feed-api.stg.topolo.us",
|
|
851
|
+
"https://flow.stg.topolo.us",
|
|
852
|
+
"https://forecast.stg.topolo.us",
|
|
853
|
+
"https://forms.stg.topolo.us",
|
|
854
|
+
"https://home.stg.topolo.us",
|
|
855
|
+
"https://insights.stg.topolo.us",
|
|
856
|
+
"https://inventory.stg.topolo.us",
|
|
857
|
+
"https://learn.stg.topolo.us",
|
|
858
|
+
"https://localize.stg.topolo.us",
|
|
859
|
+
"https://mail.stg.topolo.us",
|
|
860
|
+
"https://mcp.stg.topolo.us",
|
|
861
|
+
"https://mdm.stg.topolo.us",
|
|
862
|
+
"https://messages.stg.topolo.us",
|
|
863
|
+
"https://nexus.stg.topolo.us",
|
|
864
|
+
"https://notify.stg.topolo.us",
|
|
865
|
+
"https://nurture.stg.topolo.us",
|
|
866
|
+
"https://observability-watch.stg.topolo.us",
|
|
867
|
+
"https://p2p.stg.topolo.us",
|
|
868
|
+
"https://pay.stg.topolo.us",
|
|
869
|
+
"https://people.stg.topolo.us",
|
|
870
|
+
"https://pulse.stg.topolo.us",
|
|
871
|
+
"https://recruit.stg.topolo.us",
|
|
872
|
+
"https://render.stg.topolo.us",
|
|
873
|
+
"https://roadmap.stg.topolo.us",
|
|
874
|
+
"https://seed.stg.topolo.us",
|
|
875
|
+
"https://sign.stg.topolo.us",
|
|
876
|
+
"https://socialize-api.stg.topolo.us",
|
|
877
|
+
"https://spaces.stg.topolo.us",
|
|
878
|
+
"https://speech.stg.topolo.us",
|
|
879
|
+
"https://status.stg.topolo.us",
|
|
880
|
+
"https://studio-api.stg.topolo.us",
|
|
881
|
+
"https://studio.stg.topolo.us",
|
|
882
|
+
"https://success.stg.topolo.us",
|
|
883
|
+
"https://support.stg.topolo.us",
|
|
884
|
+
"https://survey.stg.topolo.us",
|
|
885
|
+
"https://voice.stg.topolo.us",
|
|
886
|
+
"https://web.stg.topolo.us"
|
|
887
|
+
]),
|
|
888
|
+
development: Object.freeze([
|
|
889
|
+
"https://admin.topolo.dev",
|
|
890
|
+
"https://agent.topolo.dev",
|
|
891
|
+
"https://api.one.topolo.dev",
|
|
892
|
+
"https://auth.topolo.dev",
|
|
893
|
+
"https://backup.topolo.dev",
|
|
894
|
+
"https://blog.topolo.dev",
|
|
895
|
+
"https://books.topolo.dev",
|
|
896
|
+
"https://brand.topolo.dev",
|
|
897
|
+
"https://bugfix.topolo.dev",
|
|
898
|
+
"https://bytes.topolo.dev",
|
|
899
|
+
"https://calendar.topolo.dev",
|
|
900
|
+
"https://campaigns.topolo.dev",
|
|
901
|
+
"https://capacity.topolo.dev",
|
|
902
|
+
"https://chat.topolo.dev",
|
|
903
|
+
"https://commerce.topolo.dev",
|
|
904
|
+
"https://compose.topolo.dev",
|
|
905
|
+
"https://consent.topolo.dev",
|
|
906
|
+
"https://contracts.topolo.dev",
|
|
907
|
+
"https://crm.topolo.dev",
|
|
908
|
+
"https://design.topolo.dev",
|
|
909
|
+
"https://developers.topolo.dev",
|
|
910
|
+
"https://director.topolo.dev",
|
|
911
|
+
"https://docs.topolo.dev",
|
|
912
|
+
"https://engage.topolo.dev",
|
|
913
|
+
"https://events.topolo.dev",
|
|
914
|
+
"https://feed-analytics-api.topolo.dev",
|
|
915
|
+
"https://feed-api.topolo.dev",
|
|
916
|
+
"https://flow.topolo.dev",
|
|
917
|
+
"https://forecast.topolo.dev",
|
|
918
|
+
"https://forms.topolo.dev",
|
|
919
|
+
"https://home.topolo.dev",
|
|
920
|
+
"https://insights.topolo.dev",
|
|
921
|
+
"https://inventory.topolo.dev",
|
|
922
|
+
"https://learn.topolo.dev",
|
|
923
|
+
"https://localize.topolo.dev",
|
|
924
|
+
"https://mail.topolo.dev",
|
|
925
|
+
"https://mcp.topolo.dev",
|
|
926
|
+
"https://mdm.topolo.dev",
|
|
927
|
+
"https://messages.topolo.dev",
|
|
928
|
+
"https://nexus.topolo.dev",
|
|
929
|
+
"https://notify.topolo.dev",
|
|
930
|
+
"https://nurture.topolo.dev",
|
|
931
|
+
"https://observability-watch.topolo.dev",
|
|
932
|
+
"https://p2p.topolo.dev",
|
|
933
|
+
"https://pay.topolo.dev",
|
|
934
|
+
"https://people.topolo.dev",
|
|
935
|
+
"https://pulse.topolo.dev",
|
|
936
|
+
"https://recruit.topolo.dev",
|
|
937
|
+
"https://render.topolo.dev",
|
|
938
|
+
"https://roadmap.topolo.dev",
|
|
939
|
+
"https://seed.topolo.dev",
|
|
940
|
+
"https://sign.topolo.dev",
|
|
941
|
+
"https://socialize-api.topolo.dev",
|
|
942
|
+
"https://spaces.topolo.dev",
|
|
943
|
+
"https://speech.topolo.dev",
|
|
944
|
+
"https://status.topolo.dev",
|
|
945
|
+
"https://studio-api.topolo.dev",
|
|
946
|
+
"https://studio.topolo.dev",
|
|
947
|
+
"https://success.topolo.dev",
|
|
948
|
+
"https://support.topolo.dev",
|
|
949
|
+
"https://survey.topolo.dev",
|
|
950
|
+
"https://voice.topolo.dev",
|
|
951
|
+
"https://web.topolo.dev"
|
|
952
|
+
])
|
|
953
|
+
});
|
|
954
|
+
var TOPOLO_REFUSED_HOST_SUFFIXES = Object.freeze(["sites.stg.topolo.us", "sites.topolo.app", "sites.topolo.dev", "topolo.site", "try.topolo.io", "workers.dev"]);
|
|
955
|
+
var TOPOLO_RUNTIME_ENVIRONMENTS = ["production", "staging", "development", "local"];
|
|
956
|
+
var TOPOLO_ENVIRONMENT_SERVICE_URLS = Object.freeze({
|
|
957
|
+
production: Object.freeze({
|
|
958
|
+
auth: "https://auth.topolo.app",
|
|
959
|
+
developers: "https://developers.topolo.app"
|
|
960
|
+
}),
|
|
961
|
+
staging: Object.freeze({
|
|
962
|
+
auth: "https://auth.stg.topolo.us",
|
|
963
|
+
developers: "https://developers.stg.topolo.us"
|
|
964
|
+
}),
|
|
965
|
+
development: Object.freeze({
|
|
966
|
+
auth: "https://auth.topolo.dev",
|
|
967
|
+
developers: "https://developers.topolo.dev"
|
|
968
|
+
}),
|
|
969
|
+
local: Object.freeze({})
|
|
970
|
+
});
|
|
971
|
+
var FIRST_PARTY_ORIGIN_SETS = Object.freeze({
|
|
972
|
+
production: new Set(TOPOLO_FIRST_PARTY_ORIGINS.production),
|
|
973
|
+
staging: new Set(TOPOLO_FIRST_PARTY_ORIGINS.staging),
|
|
974
|
+
development: new Set(TOPOLO_FIRST_PARTY_ORIGINS.development)
|
|
975
|
+
});
|
|
976
|
+
var LOOPBACK_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
977
|
+
function isRuntimeEnvironment(value) {
|
|
978
|
+
return typeof value === "string" && TOPOLO_RUNTIME_ENVIRONMENTS.includes(value);
|
|
979
|
+
}
|
|
980
|
+
function originIsFirstParty(url, environment) {
|
|
981
|
+
if (environment === "local") {
|
|
982
|
+
return (url.protocol === "http:" || url.protocol === "https:") && LOOPBACK_HOSTNAMES.has(url.hostname);
|
|
983
|
+
}
|
|
984
|
+
if (url.protocol !== "https:") return false;
|
|
985
|
+
return FIRST_PARTY_ORIGIN_SETS[environment].has(url.origin);
|
|
986
|
+
}
|
|
987
|
+
function requireTopoloRuntimeEnvironment(environment) {
|
|
988
|
+
if (!isRuntimeEnvironment(environment)) {
|
|
989
|
+
throw new TopoloSdkError(
|
|
990
|
+
"environment_required",
|
|
991
|
+
'Refusing to send a Topolo credential: the runtime environment is not resolved. Pass environment: "production" | "staging" | "development" | "local".'
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
return environment;
|
|
995
|
+
}
|
|
996
|
+
function assertTopoloServiceUrlEnvironment(service, value, environment) {
|
|
997
|
+
const resolved = requireTopoloRuntimeEnvironment(environment);
|
|
998
|
+
let url;
|
|
999
|
+
try {
|
|
1000
|
+
url = new URL(value);
|
|
1001
|
+
} catch {
|
|
1002
|
+
throw new TopoloSdkError("invalid_service_url", `Invalid URL configured for Topolo service "${service}".`);
|
|
1003
|
+
}
|
|
1004
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !originIsFirstParty(url, resolved)) {
|
|
1005
|
+
throw new TopoloSdkError(
|
|
1006
|
+
"environment_mismatch",
|
|
1007
|
+
`Refusing Topolo service "${service}": ${url.origin} is not a first-party ${resolved} service origin.`
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
return url.origin;
|
|
1011
|
+
}
|
|
1012
|
+
function topoloServiceOrigins(serviceUrls, environment) {
|
|
1013
|
+
return Object.fromEntries(
|
|
1014
|
+
Object.entries(serviceUrls ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(([service, value]) => [
|
|
1015
|
+
service,
|
|
1016
|
+
assertTopoloServiceUrlEnvironment(service, value, environment)
|
|
1017
|
+
])
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
1021
|
+
async function fetchWithCredentialBoundary(fetchImpl, environment, service, target, init) {
|
|
1022
|
+
const url = new URL(String(target));
|
|
1023
|
+
const origin = assertTopoloServiceUrlEnvironment(service, url.toString(), environment);
|
|
1024
|
+
const response = await fetchImpl(url.toString(), { ...init, redirect: "manual" });
|
|
1025
|
+
let responseOrigin = origin;
|
|
1026
|
+
try {
|
|
1027
|
+
if (response.url) responseOrigin = new URL(response.url).origin;
|
|
1028
|
+
} catch {
|
|
1029
|
+
responseOrigin = "";
|
|
1030
|
+
}
|
|
1031
|
+
if (response.redirected === true || responseOrigin !== origin) {
|
|
1032
|
+
await response.body?.cancel().catch(() => void 0);
|
|
1033
|
+
throw new TopoloSdkError(
|
|
1034
|
+
"credential_redirect_refused",
|
|
1035
|
+
`Refusing a redirected response from Topolo service "${service}"; credentials are never forwarded.`
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
if (REDIRECT_STATUSES.has(response.status)) {
|
|
1039
|
+
const location = response.headers.get("Location");
|
|
1040
|
+
let destination = null;
|
|
1041
|
+
if (location !== null) {
|
|
1042
|
+
try {
|
|
1043
|
+
destination = new URL(location, url).origin;
|
|
1044
|
+
} catch {
|
|
1045
|
+
destination = null;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
if (location !== null && destination !== origin) {
|
|
1049
|
+
await response.body?.cancel().catch(() => void 0);
|
|
1050
|
+
throw new TopoloSdkError(
|
|
1051
|
+
"credential_redirect_refused",
|
|
1052
|
+
`Refusing a redirect from Topolo service "${service}" to another origin; credentials are never forwarded.`
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
return response;
|
|
1057
|
+
}
|
|
1058
|
+
var MARKETING_ORIGINS = Object.freeze({
|
|
1059
|
+
development: "https://topolo.dev",
|
|
1060
|
+
staging: "https://stg.topolo.us",
|
|
1061
|
+
production: "https://topolo.io"
|
|
1062
|
+
});
|
|
1063
|
+
function isOnePublicMarketingAction(entry) {
|
|
1064
|
+
return entry.appId === "app_topolo_one" && entry.target === "marketing" && entry.authorizationMode === "public";
|
|
1065
|
+
}
|
|
1066
|
+
function publicMarketingOrigin(entry, environment) {
|
|
1067
|
+
const selected = requireTopoloRuntimeEnvironment(environment);
|
|
1068
|
+
const origin = selected === "local" ? void 0 : MARKETING_ORIGINS[selected];
|
|
1069
|
+
if (!isOnePublicMarketingAction(entry) || !origin || entry.apiBaseUrl !== origin && entry.apiBaseUrl !== origin + "/") {
|
|
1070
|
+
throw new TopoloSdkError("environment_mismatch", "Public action target does not match the registered application, target and environment.");
|
|
1071
|
+
}
|
|
1072
|
+
return origin;
|
|
1073
|
+
}
|
|
1074
|
+
async function fetchPublicMarketingAction(fetchImpl, entry, environment, target, init) {
|
|
1075
|
+
const origin = publicMarketingOrigin(entry, environment);
|
|
1076
|
+
if (target.origin !== origin || target.protocol !== "https:" || target.username || target.password || target.port) {
|
|
1077
|
+
throw new TopoloSdkError("service_path_origin_mismatch", "Public action path changes the registered target origin.");
|
|
1078
|
+
}
|
|
1079
|
+
init.signal?.throwIfAborted();
|
|
1080
|
+
const headers = new Headers({
|
|
1081
|
+
Accept: "application/json",
|
|
1082
|
+
"X-Topolo-Action-Id": entry.actionId,
|
|
1083
|
+
"X-Topolo-Action-Name": entry.name
|
|
1084
|
+
});
|
|
1085
|
+
const contentType = new Headers(init.headers).get("Content-Type");
|
|
1086
|
+
if (contentType) headers.set("Content-Type", contentType);
|
|
1087
|
+
const response = await fetchImpl(target.toString(), {
|
|
1088
|
+
method: init.method,
|
|
1089
|
+
headers,
|
|
1090
|
+
body: init.body,
|
|
1091
|
+
signal: init.signal,
|
|
1092
|
+
credentials: "omit",
|
|
1093
|
+
redirect: "manual",
|
|
1094
|
+
referrerPolicy: "no-referrer"
|
|
1095
|
+
});
|
|
1096
|
+
let responseOrigin = origin;
|
|
1097
|
+
try {
|
|
1098
|
+
if (response.url) responseOrigin = new URL(response.url).origin;
|
|
1099
|
+
} catch {
|
|
1100
|
+
responseOrigin = "";
|
|
1101
|
+
}
|
|
1102
|
+
if (response.redirected || responseOrigin !== origin || response.status >= 300 && response.status < 400) {
|
|
1103
|
+
await response.body?.cancel().catch(() => void 0);
|
|
1104
|
+
throw new TopoloSdkError("public_action_redirect_refused", "Public action redirects are refused.");
|
|
1105
|
+
}
|
|
1106
|
+
return response;
|
|
1107
|
+
}
|
|
1108
|
+
function record2(value) {
|
|
1109
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1110
|
+
}
|
|
1111
|
+
function text(value, limit = 512) {
|
|
1112
|
+
return typeof value === "string" && value.trim() ? value.trim().slice(0, limit) : null;
|
|
1113
|
+
}
|
|
1114
|
+
function strings2(value) {
|
|
1115
|
+
return Array.isArray(value) ? value.slice(0, 8).flatMap((item) => {
|
|
1116
|
+
const result = text(item);
|
|
1117
|
+
return result ? [result] : [];
|
|
1118
|
+
}) : [];
|
|
1119
|
+
}
|
|
1120
|
+
function pairs(value, first, second) {
|
|
1121
|
+
return Array.isArray(value) ? value.slice(0, 8).flatMap((item) => {
|
|
1122
|
+
if (!record2(item)) return [];
|
|
1123
|
+
const a = text(item[first], 160);
|
|
1124
|
+
const b = text(item[second]);
|
|
1125
|
+
return a && b ? [{ [first]: a, [second]: b }] : [];
|
|
1126
|
+
}) : [];
|
|
1127
|
+
}
|
|
1128
|
+
function docs(value) {
|
|
1129
|
+
const result = text(value, 2048);
|
|
1130
|
+
if (!result) return null;
|
|
1131
|
+
try {
|
|
1132
|
+
const url = new URL(result);
|
|
1133
|
+
return url.protocol === "https:" && !url.username && !url.password ? url.href : null;
|
|
1134
|
+
} catch {
|
|
1135
|
+
return null;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
function normalizeApplicationGuide(value) {
|
|
1139
|
+
if (!record2(value) || value.schemaVersion !== 1 || value.source !== "published_application_manifest") return null;
|
|
1140
|
+
const purpose = text(value.purpose, 2048);
|
|
1141
|
+
const gettingStarted = strings2(value.gettingStarted);
|
|
1142
|
+
const docsUrl = docs(value.docsUrl);
|
|
1143
|
+
return {
|
|
1144
|
+
schemaVersion: 1,
|
|
1145
|
+
source: "published_application_manifest",
|
|
1146
|
+
publishedAt: text(value.publishedAt, 160),
|
|
1147
|
+
purpose,
|
|
1148
|
+
problemStatement: text(value.problemStatement, 2048),
|
|
1149
|
+
solutionStatement: text(value.solutionStatement, 2048),
|
|
1150
|
+
benefits: strings2(value.benefits),
|
|
1151
|
+
features: pairs(value.features, "title", "description"),
|
|
1152
|
+
gettingStarted,
|
|
1153
|
+
worksWellWith: pairs(value.worksWellWith, "appId", "reason"),
|
|
1154
|
+
docsUrl,
|
|
1155
|
+
missingFields: [...!purpose ? ["purpose"] : [], ...!docsUrl ? ["docsUrl"] : [], ...!gettingStarted.length ? ["gettingStarted"] : []]
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
function applyAuthHeaders(headers, credential) {
|
|
1159
|
+
if (credential.kind === "api_key") {
|
|
1160
|
+
headers.set("X-Api-Key", credential.apiKey);
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
headers.set("Authorization", `${credential.kind === "dpop_access_token" ? "DPoP" : "Bearer"} ${credential.accessToken}`);
|
|
1164
|
+
}
|
|
1165
|
+
function applyAuditHeaders(headers, agent, requestId) {
|
|
1166
|
+
headers.set("X-Topolo-Client", `${agent.clientName}/${agent.clientVersion}`);
|
|
1167
|
+
if (agent.agentName) headers.set("X-Topolo-Agent", agent.agentName);
|
|
1168
|
+
headers.set("X-Topolo-Request-Id", requestId);
|
|
1169
|
+
}
|
|
1170
|
+
function generateRequestId() {
|
|
1171
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
1172
|
+
return crypto.randomUUID();
|
|
1173
|
+
}
|
|
1174
|
+
return `req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
1175
|
+
}
|
|
826
1176
|
var ACTION_SECRET_PREFIX = "topolo-secret:";
|
|
827
1177
|
function isActionSecretReference(value) {
|
|
828
1178
|
return typeof value === "string" && value.startsWith(ACTION_SECRET_PREFIX);
|
|
@@ -969,51 +1319,6 @@ async function createDpopProof(input2) {
|
|
|
969
1319
|
);
|
|
970
1320
|
return `${header}.${claims}.${encodeBase64Url(signature)}`;
|
|
971
1321
|
}
|
|
972
|
-
function hostnameHasSuffix(hostname, suffix) {
|
|
973
|
-
return hostname === suffix || hostname.endsWith(`.${suffix}`);
|
|
974
|
-
}
|
|
975
|
-
function workersDevAccountMatches(hostname, account) {
|
|
976
|
-
if (!hostname.endsWith(".workers.dev")) return false;
|
|
977
|
-
const labels = hostname.slice(0, -".workers.dev".length).split(".");
|
|
978
|
-
return labels.length >= 2 && labels.at(-1) === account;
|
|
979
|
-
}
|
|
980
|
-
function originMatchesEnvironment(url, environment) {
|
|
981
|
-
const hostname = url.hostname.toLowerCase();
|
|
982
|
-
if (environment === "local") {
|
|
983
|
-
return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
984
|
-
}
|
|
985
|
-
if (url.protocol !== "https:") return false;
|
|
986
|
-
if (environment === "production") {
|
|
987
|
-
return hostnameHasSuffix(hostname, "topolo.app") || hostnameHasSuffix(hostname, "topolo.io") || hostname === "api.ol0.me" || workersDevAccountMatches(hostname, "topolo");
|
|
988
|
-
}
|
|
989
|
-
if (environment === "staging") {
|
|
990
|
-
return hostnameHasSuffix(hostname, "stg.topolo.us") || workersDevAccountMatches(hostname, "topolo-staging");
|
|
991
|
-
}
|
|
992
|
-
return hostnameHasSuffix(hostname, "topolo.dev") || workersDevAccountMatches(hostname, "topolo-development");
|
|
993
|
-
}
|
|
994
|
-
function assertTopoloServiceUrlEnvironment(service, value, environment) {
|
|
995
|
-
let url;
|
|
996
|
-
try {
|
|
997
|
-
url = new URL(value);
|
|
998
|
-
} catch {
|
|
999
|
-
throw new TopoloSdkError("invalid_service_url", `Invalid URL configured for Topolo service "${service}".`);
|
|
1000
|
-
}
|
|
1001
|
-
if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !originMatchesEnvironment(url, environment)) {
|
|
1002
|
-
throw new TopoloSdkError(
|
|
1003
|
-
"environment_mismatch",
|
|
1004
|
-
`Refusing Topolo service "${service}": ${url.origin} does not belong to ${environment}.`
|
|
1005
|
-
);
|
|
1006
|
-
}
|
|
1007
|
-
return url.origin;
|
|
1008
|
-
}
|
|
1009
|
-
function topoloServiceOrigins(serviceUrls, environment) {
|
|
1010
|
-
return Object.fromEntries(
|
|
1011
|
-
Object.entries(serviceUrls ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(([service, value]) => [
|
|
1012
|
-
service,
|
|
1013
|
-
assertTopoloServiceUrlEnvironment(service, value, environment)
|
|
1014
|
-
])
|
|
1015
|
-
);
|
|
1016
|
-
}
|
|
1017
1322
|
var APP_API_URLS = {
|
|
1018
1323
|
auth: "https://auth.topolo.app",
|
|
1019
1324
|
developers: "https://developers.topolo.app"
|
|
@@ -1036,9 +1341,15 @@ function resolveCatalogServiceUrl(entry, options = {}) {
|
|
|
1036
1341
|
if (apiBaseUrl) return apiBaseUrl;
|
|
1037
1342
|
return null;
|
|
1038
1343
|
}
|
|
1039
|
-
function resolveServiceUrl(service, overrides) {
|
|
1344
|
+
function resolveServiceUrl(service, overrides, environment) {
|
|
1040
1345
|
const override = resolveServiceUrlOverride([service], overrides);
|
|
1041
1346
|
if (override) return override;
|
|
1347
|
+
if (environment !== void 0) {
|
|
1348
|
+
const selected = requireTopoloRuntimeEnvironment(environment);
|
|
1349
|
+
const origin = TOPOLO_ENVIRONMENT_SERVICE_URLS[selected][service];
|
|
1350
|
+
if (!origin) throw new TopoloSdkError("service_url_required", `Configure an explicit ${service} origin for ${selected}.`);
|
|
1351
|
+
return origin;
|
|
1352
|
+
}
|
|
1042
1353
|
return DEFAULT_APP_URLS[service];
|
|
1043
1354
|
}
|
|
1044
1355
|
function catalogServiceKeyCandidates(entry, preferred) {
|
|
@@ -1082,12 +1393,15 @@ function firstString(...values) {
|
|
|
1082
1393
|
}
|
|
1083
1394
|
return null;
|
|
1084
1395
|
}
|
|
1396
|
+
var DATA_CLASSIFICATIONS = /* @__PURE__ */ new Set(["public", "internal", "confidential", "restricted", "public_link"]);
|
|
1397
|
+
var TOPOLO_REFERENCE_PATTERN = /^topolo:app_[a-z0-9]+(?:_[a-z0-9]+)+\/[A-Za-z0-9][A-Za-z0-9_-]{0,127}\/[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*\/[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}(?:@([1-9][0-9]*))?(?![\s\S])/;
|
|
1085
1398
|
var SUPPORTED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
|
|
1086
1399
|
"$schema",
|
|
1087
1400
|
"$id",
|
|
1088
1401
|
"$ref",
|
|
1089
1402
|
"$defs",
|
|
1090
1403
|
"definitions",
|
|
1404
|
+
"dataClassification",
|
|
1091
1405
|
"title",
|
|
1092
1406
|
"description",
|
|
1093
1407
|
"default",
|
|
@@ -1138,7 +1452,10 @@ function inspectSchema(schema, path, root, depth) {
|
|
|
1138
1452
|
if (unsupportedKeyword) {
|
|
1139
1453
|
return { path, code: "unsupported_schema_keyword", message: `Action schema uses unsupported keyword ${unsupportedKeyword}.` };
|
|
1140
1454
|
}
|
|
1141
|
-
if (
|
|
1455
|
+
if (Object.hasOwn(schema, "dataClassification") && !DATA_CLASSIFICATIONS.has(schema.dataClassification)) {
|
|
1456
|
+
return { path: `${path}.dataClassification`, code: "invalid_data_classification", message: "Schema dataClassification must be public, internal, confidential, restricted or public_link." };
|
|
1457
|
+
}
|
|
1458
|
+
if (typeof schema.format === "string" && !["uri", "email", "date-time", "uuid", "topolo-reference"].includes(schema.format)) {
|
|
1142
1459
|
return { path, code: "unsupported_schema_format", message: `Action schema uses unsupported format ${schema.format}.` };
|
|
1143
1460
|
}
|
|
1144
1461
|
if (typeof schema.$ref === "string" && !resolveLocalReference(root, schema.$ref)) {
|
|
@@ -1300,6 +1617,10 @@ function validateString(schema, value, path, errors) {
|
|
|
1300
1617
|
errors.push({ path, code: "invalid_schema_pattern", message: "Action schema contains an invalid pattern." });
|
|
1301
1618
|
}
|
|
1302
1619
|
}
|
|
1620
|
+
if (schema.format === "topolo-reference") {
|
|
1621
|
+
const match = TOPOLO_REFERENCE_PATTERN.exec(value);
|
|
1622
|
+
if (!match || match[1] !== void 0 && !Number.isSafeInteger(Number(match[1]))) errors.push({ path, code: "format_topolo_reference", message: "Expected a canonical Topolo resource reference." });
|
|
1623
|
+
}
|
|
1303
1624
|
if (schema.format === "uri" && !isUrl(value)) errors.push({ path, code: "format_uri", message: "Expected an absolute URL." });
|
|
1304
1625
|
if (schema.format === "email" && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(value)) errors.push({ path, code: "format_email", message: "Expected an email address." });
|
|
1305
1626
|
if (schema.format === "date-time" && !isDateTime(value)) errors.push({ path, code: "format_date_time", message: "Expected an ISO-8601 date-time." });
|
|
@@ -1869,6 +2190,17 @@ var TopoloClient = class _TopoloClient {
|
|
|
1869
2190
|
* use and for the generic `topolo api` / MCP passthrough tool.
|
|
1870
2191
|
*/
|
|
1871
2192
|
async request(opts) {
|
|
2193
|
+
return this.requestWithTarget(opts);
|
|
2194
|
+
}
|
|
2195
|
+
async requestWithTarget(opts, publicAction) {
|
|
2196
|
+
requireTopoloRuntimeEnvironment(this.environment);
|
|
2197
|
+
const publicOrigin = publicAction ? publicMarketingOrigin(publicAction, this.environment) : void 0;
|
|
2198
|
+
if (publicOrigin && (opts.actorEnvelope !== void 0 || opts.resource !== void 0)) {
|
|
2199
|
+
throw new TopoloSdkError("public_action_context_not_supported", "Public marketing actions do not accept actor or resource authority.");
|
|
2200
|
+
}
|
|
2201
|
+
if (publicOrigin && (!opts.path.startsWith("/") || opts.path.startsWith("//") || opts.path.includes("\\"))) {
|
|
2202
|
+
throw new TopoloSdkError("service_path_origin_mismatch", "Public action paths must be origin-relative paths.");
|
|
2203
|
+
}
|
|
1872
2204
|
const method = opts.method ?? "GET";
|
|
1873
2205
|
if (this.requireConfirmForWrites && WRITE_METHODS.has(method) && !opts.confirm) {
|
|
1874
2206
|
throw new TopoloConfirmationRequiredError(
|
|
@@ -1877,7 +2209,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1877
2209
|
}
|
|
1878
2210
|
const actionId = requestActionId(opts.headers);
|
|
1879
2211
|
const taskId = opts.actorEnvelope?.delegation?.taskId ?? this.actorContext?.taskId;
|
|
1880
|
-
let requestCredential = await this.loadCurrentCredential({
|
|
2212
|
+
let requestCredential = publicOrigin ? void 0 : await this.loadCurrentCredential({
|
|
1881
2213
|
service: normalizeLookupKey(opts.service),
|
|
1882
2214
|
method,
|
|
1883
2215
|
path: opts.path,
|
|
@@ -1885,7 +2217,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1885
2217
|
...taskId ? { taskId } : {},
|
|
1886
2218
|
...opts.resource ? { resource: opts.resource } : {}
|
|
1887
2219
|
});
|
|
1888
|
-
const resolvedService = opts.baseUrl ? { serviceKey: normalizeLookupKey(opts.service), baseUrl: this.validatedServiceOrigin(opts.service, opts.baseUrl) } : await this.resolveRequestService(opts.service);
|
|
2220
|
+
const resolvedService = publicOrigin ? { serviceKey: normalizeLookupKey(opts.service), baseUrl: publicOrigin } : opts.baseUrl ? { serviceKey: normalizeLookupKey(opts.service), baseUrl: this.validatedServiceOrigin(opts.service, opts.baseUrl) } : await this.resolveRequestService(opts.service);
|
|
1889
2221
|
const baseUrl = resolvedService.baseUrl;
|
|
1890
2222
|
const url = new URL(opts.path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
1891
2223
|
if (url.origin !== new URL(baseUrl).origin) {
|
|
@@ -1906,17 +2238,21 @@ var TopoloClient = class _TopoloClient {
|
|
|
1906
2238
|
headers.set("Accept", "application/json");
|
|
1907
2239
|
if (opts.body !== void 0) headers.set("Content-Type", "application/json");
|
|
1908
2240
|
const platformAppId = resolveConfiguredAppId(resolvedService.serviceKey, this.appIds);
|
|
1909
|
-
if (platformAppId) headers.set("X-App-ID", platformAppId);
|
|
1910
|
-
const actorEnvelope = opts.actorEnvelope === void 0 ? void 0 : parseActorEnvelope(opts.actorEnvelope);
|
|
2241
|
+
if (platformAppId && !publicOrigin) headers.set("X-App-ID", platformAppId);
|
|
2242
|
+
const actorEnvelope = publicOrigin || opts.actorEnvelope === void 0 ? void 0 : parseActorEnvelope(opts.actorEnvelope);
|
|
1911
2243
|
const requestId = actorEnvelope?.action.requestId ?? generateRequestId();
|
|
1912
|
-
applyAuditHeaders(headers, this.agent, requestId);
|
|
1913
|
-
if (
|
|
2244
|
+
if (!publicOrigin) applyAuditHeaders(headers, this.agent, requestId);
|
|
2245
|
+
if (publicAction) {
|
|
2246
|
+
headers.set(TOPOLO_ACTION_ID_HEADER, publicAction.actionId);
|
|
2247
|
+
headers.set(TOPOLO_ACTION_NAME_HEADER, publicAction.name);
|
|
2248
|
+
}
|
|
2249
|
+
if (opts.headers && !publicOrigin) {
|
|
1914
2250
|
for (const [k, v] of Object.entries(opts.headers)) headers.set(k, v);
|
|
1915
2251
|
}
|
|
1916
2252
|
if (actorEnvelope) {
|
|
1917
2253
|
headers.set(TOPOLO_ACTOR_ENVELOPE_HEADER, encodeActorEnvelopeHeader(actorEnvelope));
|
|
1918
2254
|
}
|
|
1919
|
-
if (opts.resource) {
|
|
2255
|
+
if (opts.resource && !publicOrigin) {
|
|
1920
2256
|
const resourceType = opts.resource.resourceType.trim();
|
|
1921
2257
|
const resourceId = opts.resource.resourceId.trim();
|
|
1922
2258
|
if (!resourceType || !resourceId) {
|
|
@@ -1941,8 +2277,8 @@ var TopoloClient = class _TopoloClient {
|
|
|
1941
2277
|
const signal = opts.signal ? mergeSignals(opts.signal, controller.signal) : controller.signal;
|
|
1942
2278
|
try {
|
|
1943
2279
|
const requestHeaders = new Headers(headers);
|
|
1944
|
-
applyAuthHeaders(requestHeaders, requestCredential);
|
|
1945
|
-
if (requestCredential
|
|
2280
|
+
if (requestCredential) applyAuthHeaders(requestHeaders, requestCredential);
|
|
2281
|
+
if (requestCredential?.kind === "dpop_access_token") {
|
|
1946
2282
|
requestHeaders.set("DPoP", await createDpopProof({
|
|
1947
2283
|
accessToken: requestCredential.accessToken,
|
|
1948
2284
|
method,
|
|
@@ -1950,12 +2286,13 @@ var TopoloClient = class _TopoloClient {
|
|
|
1950
2286
|
privateJwk: requestCredential.privateJwk
|
|
1951
2287
|
}));
|
|
1952
2288
|
}
|
|
1953
|
-
|
|
2289
|
+
const init = {
|
|
1954
2290
|
method,
|
|
1955
2291
|
headers: requestHeaders,
|
|
1956
2292
|
body: opts.rawBody ?? (opts.body !== void 0 ? JSON.stringify(opts.body) : null),
|
|
1957
2293
|
signal
|
|
1958
|
-
}
|
|
2294
|
+
};
|
|
2295
|
+
return publicOrigin ? await fetchPublicMarketingAction((input2, requestInit) => this.fetchImpl(input2, requestInit), publicAction, this.environment, url, init) : await this.credentialedFetch(resolvedService.serviceKey, urlStr, init);
|
|
1959
2296
|
} catch (error) {
|
|
1960
2297
|
if (timedOut) {
|
|
1961
2298
|
throw new TopoloSdkError(
|
|
@@ -1963,6 +2300,9 @@ var TopoloClient = class _TopoloClient {
|
|
|
1963
2300
|
`Topolo request timed out after ${this.timeoutMs}ms. Verify current state before retrying a mutation.`
|
|
1964
2301
|
);
|
|
1965
2302
|
}
|
|
2303
|
+
if (publicOrigin && opts.signal?.aborted) {
|
|
2304
|
+
throw new TopoloSdkError("request_cancelled", "Public action request was cancelled.");
|
|
2305
|
+
}
|
|
1966
2306
|
throw error;
|
|
1967
2307
|
} finally {
|
|
1968
2308
|
clearTimeout(timeoutId);
|
|
@@ -1985,7 +2325,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1985
2325
|
try {
|
|
1986
2326
|
const attemptedCredential = requestCredential;
|
|
1987
2327
|
res = await fetchWithReadRetry();
|
|
1988
|
-
if (res.status === 401 && attemptedCredential
|
|
2328
|
+
if (res.status === 401 && attemptedCredential?.kind === "access_token" && this.refreshCredential) {
|
|
1989
2329
|
await res.body?.cancel().catch(() => void 0);
|
|
1990
2330
|
const refreshed = await this.refreshRejectedAccessToken(attemptedCredential.accessToken);
|
|
1991
2331
|
if (refreshed) {
|
|
@@ -2132,10 +2472,16 @@ var TopoloClient = class _TopoloClient {
|
|
|
2132
2472
|
return this.refreshInFlight;
|
|
2133
2473
|
}
|
|
2134
2474
|
validatedServiceOrigin(service, value) {
|
|
2135
|
-
|
|
2136
|
-
|
|
2475
|
+
return assertTopoloServiceUrlEnvironment(service, requireHttpOrigin(value), this.environment);
|
|
2476
|
+
}
|
|
2477
|
+
/** The only door to the network for anything this client sends; see fetchWithCredentialBoundary. */
|
|
2478
|
+
credentialedFetch(service, target, init) {
|
|
2479
|
+
return fetchWithCredentialBoundary((input2, requestInit) => this.fetchImpl(input2, requestInit), this.environment, service, target, init);
|
|
2137
2480
|
}
|
|
2138
2481
|
validatedActionEntry(entry) {
|
|
2482
|
+
if (isOnePublicMarketingAction(entry)) {
|
|
2483
|
+
return { ...entry, apiBaseUrl: publicMarketingOrigin(entry, this.environment) };
|
|
2484
|
+
}
|
|
2139
2485
|
if (!entry.apiBaseUrl) return entry;
|
|
2140
2486
|
return {
|
|
2141
2487
|
...entry,
|
|
@@ -2156,7 +2502,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2156
2502
|
}
|
|
2157
2503
|
return {
|
|
2158
2504
|
serviceKey,
|
|
2159
|
-
baseUrl: this.validatedServiceOrigin(serviceKey,
|
|
2505
|
+
baseUrl: this.validatedServiceOrigin(serviceKey, resolveServiceUrl(serviceKey, this.serviceUrls, this.environment))
|
|
2160
2506
|
};
|
|
2161
2507
|
}
|
|
2162
2508
|
const live = await this.resolveLiveCallableService(serviceKey);
|
|
@@ -2199,7 +2545,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2199
2545
|
if (!normalized) return null;
|
|
2200
2546
|
const configuredAppId = resolveConfiguredAppId(normalized, this.appIds);
|
|
2201
2547
|
if (configuredAppId) return configuredAppId;
|
|
2202
|
-
const authBase = this.validatedServiceOrigin("developers", resolveServiceUrl("developers", this.serviceUrls));
|
|
2548
|
+
const authBase = this.validatedServiceOrigin("developers", resolveServiceUrl("developers", this.serviceUrls, this.environment));
|
|
2203
2549
|
const cacheKey = `${authBase}|${normalized}`;
|
|
2204
2550
|
const now = Date.now();
|
|
2205
2551
|
const cached = _TopoloClient.appIdCache.get(cacheKey);
|
|
@@ -2212,7 +2558,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2212
2558
|
).toString();
|
|
2213
2559
|
let res;
|
|
2214
2560
|
try {
|
|
2215
|
-
res = await this.
|
|
2561
|
+
res = await this.credentialedFetch("developers", url, {
|
|
2216
2562
|
method: "GET",
|
|
2217
2563
|
headers: { Accept: "application/json" }
|
|
2218
2564
|
});
|
|
@@ -2377,7 +2723,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2377
2723
|
"TopoloAuth did not return an action catalog for the current credential."
|
|
2378
2724
|
);
|
|
2379
2725
|
}
|
|
2380
|
-
const entries = raw.map((item) => normalizeActionCatalogEntry(item)).filter((entry) => Boolean(entry))
|
|
2726
|
+
const entries = raw.map((item) => normalizeActionCatalogEntry(item)).filter((entry) => Boolean(entry));
|
|
2381
2727
|
return {
|
|
2382
2728
|
actions: entries,
|
|
2383
2729
|
page: normalizeCatalogPage(
|
|
@@ -2442,11 +2788,18 @@ var TopoloClient = class _TopoloClient {
|
|
|
2442
2788
|
const raw = res.data?.action ?? res.action;
|
|
2443
2789
|
const match = normalizeActionCatalogEntry(raw);
|
|
2444
2790
|
if (!match) throw new TopoloSdkError("unknown_action", `Unknown or unavailable Topolo action "${action}".`);
|
|
2791
|
+
if (isOnePublicMarketingAction(match) && match.actionId !== actionId) {
|
|
2792
|
+
throw new TopoloSdkError("action_contract_mismatch", "Public action catalog response does not match the requested action ID.");
|
|
2793
|
+
}
|
|
2445
2794
|
return this.validatedActionEntry(match);
|
|
2446
2795
|
}
|
|
2447
2796
|
async callAction(action, input2 = {}, options = {}) {
|
|
2448
|
-
const entry = await this.getAction(action);
|
|
2449
|
-
|
|
2797
|
+
const entry = Object.freeze({ ...await this.getAction(action) });
|
|
2798
|
+
const publicAction = isOnePublicMarketingAction(entry);
|
|
2799
|
+
if (publicAction && entry.actionId !== action.trim()) {
|
|
2800
|
+
throw new TopoloSdkError("action_contract_mismatch", "Public action catalog response does not match the requested action ID.");
|
|
2801
|
+
}
|
|
2802
|
+
return this.executeResolvedAction(entry, input2, options, false, publicAction);
|
|
2450
2803
|
}
|
|
2451
2804
|
async issueExternalAgentCredential(input2) {
|
|
2452
2805
|
const actionId = "app_topolo_auth.principal_credentials.issue";
|
|
@@ -2496,7 +2849,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2496
2849
|
}
|
|
2497
2850
|
async putUrlSessionContent(entry, uploadUrl, method, bytes, options) {
|
|
2498
2851
|
const service = entry.appSlug || entry.appId;
|
|
2499
|
-
const resolved = entry.apiBaseUrl ? { serviceKey: normalizeLookupKey(service), baseUrl:
|
|
2852
|
+
const resolved = entry.apiBaseUrl ? { serviceKey: normalizeLookupKey(service), baseUrl: this.validatedServiceOrigin(service, entry.apiBaseUrl) } : await this.resolveRequestService(service);
|
|
2500
2853
|
const target = new URL(uploadUrl, resolved.baseUrl.endsWith("/") ? resolved.baseUrl : `${resolved.baseUrl}/`);
|
|
2501
2854
|
const body = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
2502
2855
|
if (target.origin === new URL(resolved.baseUrl).origin) {
|
|
@@ -2512,7 +2865,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2512
2865
|
});
|
|
2513
2866
|
return;
|
|
2514
2867
|
}
|
|
2515
|
-
const response = await this.
|
|
2868
|
+
const response = await this.credentialedFetch(service, target, {
|
|
2516
2869
|
method,
|
|
2517
2870
|
headers: { "Content-Type": options.contentType.trim().toLowerCase() },
|
|
2518
2871
|
body
|
|
@@ -2590,7 +2943,15 @@ var TopoloClient = class _TopoloClient {
|
|
|
2590
2943
|
}
|
|
2591
2944
|
return this.executeResolvedAction(entry, input2, options);
|
|
2592
2945
|
}
|
|
2593
|
-
async executeResolvedAction(entry, input2, options, preserveSensitiveResult = false) {
|
|
2946
|
+
async executeResolvedAction(entry, input2, options, preserveSensitiveResult = false, allowPublicMarketing = false) {
|
|
2947
|
+
if (isOnePublicMarketingAction(entry)) {
|
|
2948
|
+
if (!allowPublicMarketing) {
|
|
2949
|
+
throw new TopoloSdkError("public_action_entrypoint_required", "Public marketing actions require callAction.");
|
|
2950
|
+
}
|
|
2951
|
+
if (options.actorEnvelope !== void 0 || options.resource !== void 0) {
|
|
2952
|
+
throw new TopoloSdkError("public_action_context_not_supported", "Public marketing actions do not accept actor or resource authority.");
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2594
2955
|
const agentAccess = entry.agentAccess ?? (entry.destructive ? "off" : entry.readOnly && !entry.requiresConfirmation ? "auto" : "confirm");
|
|
2595
2956
|
if (agentAccess === "off") {
|
|
2596
2957
|
throw new TopoloPermissionError(
|
|
@@ -2619,7 +2980,8 @@ var TopoloClient = class _TopoloClient {
|
|
|
2619
2980
|
);
|
|
2620
2981
|
}
|
|
2621
2982
|
const mapped = mapActionInput(entry, input2);
|
|
2622
|
-
const
|
|
2983
|
+
const publicAction = isOnePublicMarketingAction(entry);
|
|
2984
|
+
const resolvedActorEnvelope = publicAction ? void 0 : options.actorEnvelope ?? await this.buildVerifiedAgentActorEnvelope(entry.actionId, input2, options.resource);
|
|
2623
2985
|
if (resolvedActorEnvelope) {
|
|
2624
2986
|
const actorEnvelope = parseActorEnvelope(resolvedActorEnvelope);
|
|
2625
2987
|
if (actorEnvelope.action.actionId !== entry.actionId) {
|
|
@@ -2636,8 +2998,9 @@ var TopoloClient = class _TopoloClient {
|
|
|
2636
2998
|
const service = entry.appSlug || entry.appId;
|
|
2637
2999
|
let result;
|
|
2638
3000
|
try {
|
|
2639
|
-
result = await this.
|
|
3001
|
+
result = await this.requestWithTarget({
|
|
2640
3002
|
service,
|
|
3003
|
+
signal: options.signal,
|
|
2641
3004
|
...entry.apiBaseUrl ? { baseUrl: entry.apiBaseUrl } : {},
|
|
2642
3005
|
method: entry.method,
|
|
2643
3006
|
path: mapped.path,
|
|
@@ -2650,7 +3013,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2650
3013
|
[TOPOLO_ACTION_NAME_HEADER]: entry.name
|
|
2651
3014
|
},
|
|
2652
3015
|
confirm: options.confirm === true || entry.readOnly && !entry.destructive && !entry.requiresConfirmation
|
|
2653
|
-
});
|
|
3016
|
+
}, publicAction ? entry : void 0);
|
|
2654
3017
|
} catch (error) {
|
|
2655
3018
|
throw maskActionSecretError(error, this.actionSecrets);
|
|
2656
3019
|
}
|
|
@@ -3448,9 +3811,11 @@ var DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
|
3448
3811
|
var TopoloOAuth = class {
|
|
3449
3812
|
baseUrl;
|
|
3450
3813
|
fetchImpl;
|
|
3814
|
+
environment;
|
|
3451
3815
|
constructor(options = {}) {
|
|
3452
|
-
this.baseUrl =
|
|
3816
|
+
this.baseUrl = resolveServiceUrl("auth", options.serviceUrls, options.environment);
|
|
3453
3817
|
this.fetchImpl = options.fetch ?? fetch;
|
|
3818
|
+
this.environment = options.environment;
|
|
3454
3819
|
}
|
|
3455
3820
|
/**
|
|
3456
3821
|
* RFC 8628 device authorization grant — starts the flow by asking the server
|
|
@@ -3515,7 +3880,7 @@ var TopoloOAuth = class {
|
|
|
3515
3880
|
}
|
|
3516
3881
|
async #postForm(path, body) {
|
|
3517
3882
|
const url = new URL(path, this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`);
|
|
3518
|
-
const res = await this.fetchImpl
|
|
3883
|
+
const res = await fetchWithCredentialBoundary(this.fetchImpl, this.environment, "auth", url, {
|
|
3519
3884
|
method: "POST",
|
|
3520
3885
|
headers: {
|
|
3521
3886
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
@@ -4044,7 +4409,7 @@ async function pollUntilApproved(params) {
|
|
|
4044
4409
|
// src/commands/auth-lock.ts
|
|
4045
4410
|
import { createHash as createHash3 } from "crypto";
|
|
4046
4411
|
|
|
4047
|
-
// node_modules
|
|
4412
|
+
// node_modules/@topolo/sdk/dist/node/credential-store.js
|
|
4048
4413
|
import { mkdir as mkdir2, readFile as readFile2, chmod as chmod2, lstat, readdir as readdir2 } from "fs/promises";
|
|
4049
4414
|
import { existsSync as existsSync2, mkdirSync, lstatSync, readdirSync, readFileSync as readFileSync2, openSync, writeFileSync, fsyncSync, closeSync, renameSync, unlinkSync, rmdirSync, linkSync } from "fs";
|
|
4050
4415
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
|
|
@@ -4386,55 +4751,256 @@ var TopoloReauthRequiredError2 = class extends TopoloAuthError2 {
|
|
|
4386
4751
|
this.reauthorization = Object.freeze({ ...reauthorization });
|
|
4387
4752
|
}
|
|
4388
4753
|
};
|
|
4389
|
-
var
|
|
4390
|
-
|
|
4391
|
-
|
|
4754
|
+
var TOPOLO_FIRST_PARTY_ORIGINS2 = Object.freeze({
|
|
4755
|
+
production: Object.freeze([
|
|
4756
|
+
"https://admin.topolo.app",
|
|
4757
|
+
"https://agent.topolo.app",
|
|
4758
|
+
"https://api.ol0.me",
|
|
4759
|
+
"https://auth.topolo.app",
|
|
4760
|
+
"https://backup.topolo.app",
|
|
4761
|
+
"https://blog.topolo.app",
|
|
4762
|
+
"https://books.topolo.app",
|
|
4763
|
+
"https://brand.topolo.app",
|
|
4764
|
+
"https://bugfix.topolo.app",
|
|
4765
|
+
"https://bytes.topolo.app",
|
|
4766
|
+
"https://calendar.topolo.app",
|
|
4767
|
+
"https://campaigns.topolo.app",
|
|
4768
|
+
"https://capacity.topolo.app",
|
|
4769
|
+
"https://chat.topolo.app",
|
|
4770
|
+
"https://commerce.topolo.app",
|
|
4771
|
+
"https://compose.topolo.app",
|
|
4772
|
+
"https://consent.topolo.app",
|
|
4773
|
+
"https://contracts.topolo.app",
|
|
4774
|
+
"https://crm.topolo.app",
|
|
4775
|
+
"https://design.topolo.app",
|
|
4776
|
+
"https://developers.topolo.app",
|
|
4777
|
+
"https://director.topolo.app",
|
|
4778
|
+
"https://docs.topolo.app",
|
|
4779
|
+
"https://engage.topolo.app",
|
|
4780
|
+
"https://events.topolo.app",
|
|
4781
|
+
"https://feed-analytics-api.topolo.app",
|
|
4782
|
+
"https://feed-api.topolo.app",
|
|
4783
|
+
"https://flow.topolo.app",
|
|
4784
|
+
"https://forecast.topolo.app",
|
|
4785
|
+
"https://forms.topolo.app",
|
|
4786
|
+
"https://home.topolo.app",
|
|
4787
|
+
"https://insights.topolo.app",
|
|
4788
|
+
"https://inventory.topolo.app",
|
|
4789
|
+
"https://learn.topolo.app",
|
|
4790
|
+
"https://localize.topolo.app",
|
|
4791
|
+
"https://mail.topolo.app",
|
|
4792
|
+
"https://mcp.topolo.app",
|
|
4793
|
+
"https://mdm.topolo.app",
|
|
4794
|
+
"https://messages.topolo.app",
|
|
4795
|
+
"https://nexus.topolo.app",
|
|
4796
|
+
"https://notify.topolo.app",
|
|
4797
|
+
"https://nurture.topolo.app",
|
|
4798
|
+
"https://observability-watch.topolo.app",
|
|
4799
|
+
"https://p2p.topolo.app",
|
|
4800
|
+
"https://pay.topolo.app",
|
|
4801
|
+
"https://people.topolo.app",
|
|
4802
|
+
"https://quro.topolo.app",
|
|
4803
|
+
"https://recruit.topolo.app",
|
|
4804
|
+
"https://render.topolo.app",
|
|
4805
|
+
"https://roadmap.topolo.app",
|
|
4806
|
+
"https://sign.topolo.app",
|
|
4807
|
+
"https://socialize-api.topolo.app",
|
|
4808
|
+
"https://spaces.topolo.app",
|
|
4809
|
+
"https://speech.topolo.app",
|
|
4810
|
+
"https://status.topolo.app",
|
|
4811
|
+
"https://studio.topolo.app",
|
|
4812
|
+
"https://success.topolo.app",
|
|
4813
|
+
"https://support.topolo.app",
|
|
4814
|
+
"https://survey.topolo.app",
|
|
4815
|
+
"https://voice.topolo.app",
|
|
4816
|
+
"https://web.topolo.app",
|
|
4817
|
+
"https://www.topolo.app"
|
|
4818
|
+
]),
|
|
4819
|
+
staging: Object.freeze([
|
|
4820
|
+
"https://admin.stg.topolo.us",
|
|
4821
|
+
"https://agent.stg.topolo.us",
|
|
4822
|
+
"https://api.one.stg.topolo.us",
|
|
4823
|
+
"https://auth.stg.topolo.us",
|
|
4824
|
+
"https://backup.stg.topolo.us",
|
|
4825
|
+
"https://blog.stg.topolo.us",
|
|
4826
|
+
"https://books.stg.topolo.us",
|
|
4827
|
+
"https://brand.stg.topolo.us",
|
|
4828
|
+
"https://bugfix.stg.topolo.us",
|
|
4829
|
+
"https://bytes.stg.topolo.us",
|
|
4830
|
+
"https://calendar.stg.topolo.us",
|
|
4831
|
+
"https://campaigns.stg.topolo.us",
|
|
4832
|
+
"https://capacity.stg.topolo.us",
|
|
4833
|
+
"https://chat.stg.topolo.us",
|
|
4834
|
+
"https://commerce.stg.topolo.us",
|
|
4835
|
+
"https://compose.stg.topolo.us",
|
|
4836
|
+
"https://consent.stg.topolo.us",
|
|
4837
|
+
"https://contracts.stg.topolo.us",
|
|
4838
|
+
"https://crm.stg.topolo.us",
|
|
4839
|
+
"https://design.stg.topolo.us",
|
|
4840
|
+
"https://developers.stg.topolo.us",
|
|
4841
|
+
"https://director.stg.topolo.us",
|
|
4842
|
+
"https://docs.stg.topolo.us",
|
|
4843
|
+
"https://engage.stg.topolo.us",
|
|
4844
|
+
"https://events.stg.topolo.us",
|
|
4845
|
+
"https://feed-analytics-api.stg.topolo.us",
|
|
4846
|
+
"https://feed-api.stg.topolo.us",
|
|
4847
|
+
"https://flow.stg.topolo.us",
|
|
4848
|
+
"https://forecast.stg.topolo.us",
|
|
4849
|
+
"https://forms.stg.topolo.us",
|
|
4850
|
+
"https://home.stg.topolo.us",
|
|
4851
|
+
"https://insights.stg.topolo.us",
|
|
4852
|
+
"https://inventory.stg.topolo.us",
|
|
4853
|
+
"https://learn.stg.topolo.us",
|
|
4854
|
+
"https://localize.stg.topolo.us",
|
|
4855
|
+
"https://mail.stg.topolo.us",
|
|
4856
|
+
"https://mcp.stg.topolo.us",
|
|
4857
|
+
"https://mdm.stg.topolo.us",
|
|
4858
|
+
"https://messages.stg.topolo.us",
|
|
4859
|
+
"https://nexus.stg.topolo.us",
|
|
4860
|
+
"https://notify.stg.topolo.us",
|
|
4861
|
+
"https://nurture.stg.topolo.us",
|
|
4862
|
+
"https://observability-watch.stg.topolo.us",
|
|
4863
|
+
"https://p2p.stg.topolo.us",
|
|
4864
|
+
"https://pay.stg.topolo.us",
|
|
4865
|
+
"https://people.stg.topolo.us",
|
|
4866
|
+
"https://pulse.stg.topolo.us",
|
|
4867
|
+
"https://recruit.stg.topolo.us",
|
|
4868
|
+
"https://render.stg.topolo.us",
|
|
4869
|
+
"https://roadmap.stg.topolo.us",
|
|
4870
|
+
"https://seed.stg.topolo.us",
|
|
4871
|
+
"https://sign.stg.topolo.us",
|
|
4872
|
+
"https://socialize-api.stg.topolo.us",
|
|
4873
|
+
"https://spaces.stg.topolo.us",
|
|
4874
|
+
"https://speech.stg.topolo.us",
|
|
4875
|
+
"https://status.stg.topolo.us",
|
|
4876
|
+
"https://studio-api.stg.topolo.us",
|
|
4877
|
+
"https://studio.stg.topolo.us",
|
|
4878
|
+
"https://success.stg.topolo.us",
|
|
4879
|
+
"https://support.stg.topolo.us",
|
|
4880
|
+
"https://survey.stg.topolo.us",
|
|
4881
|
+
"https://voice.stg.topolo.us",
|
|
4882
|
+
"https://web.stg.topolo.us"
|
|
4883
|
+
]),
|
|
4884
|
+
development: Object.freeze([
|
|
4885
|
+
"https://admin.topolo.dev",
|
|
4886
|
+
"https://agent.topolo.dev",
|
|
4887
|
+
"https://api.one.topolo.dev",
|
|
4888
|
+
"https://auth.topolo.dev",
|
|
4889
|
+
"https://backup.topolo.dev",
|
|
4890
|
+
"https://blog.topolo.dev",
|
|
4891
|
+
"https://books.topolo.dev",
|
|
4892
|
+
"https://brand.topolo.dev",
|
|
4893
|
+
"https://bugfix.topolo.dev",
|
|
4894
|
+
"https://bytes.topolo.dev",
|
|
4895
|
+
"https://calendar.topolo.dev",
|
|
4896
|
+
"https://campaigns.topolo.dev",
|
|
4897
|
+
"https://capacity.topolo.dev",
|
|
4898
|
+
"https://chat.topolo.dev",
|
|
4899
|
+
"https://commerce.topolo.dev",
|
|
4900
|
+
"https://compose.topolo.dev",
|
|
4901
|
+
"https://consent.topolo.dev",
|
|
4902
|
+
"https://contracts.topolo.dev",
|
|
4903
|
+
"https://crm.topolo.dev",
|
|
4904
|
+
"https://design.topolo.dev",
|
|
4905
|
+
"https://developers.topolo.dev",
|
|
4906
|
+
"https://director.topolo.dev",
|
|
4907
|
+
"https://docs.topolo.dev",
|
|
4908
|
+
"https://engage.topolo.dev",
|
|
4909
|
+
"https://events.topolo.dev",
|
|
4910
|
+
"https://feed-analytics-api.topolo.dev",
|
|
4911
|
+
"https://feed-api.topolo.dev",
|
|
4912
|
+
"https://flow.topolo.dev",
|
|
4913
|
+
"https://forecast.topolo.dev",
|
|
4914
|
+
"https://forms.topolo.dev",
|
|
4915
|
+
"https://home.topolo.dev",
|
|
4916
|
+
"https://insights.topolo.dev",
|
|
4917
|
+
"https://inventory.topolo.dev",
|
|
4918
|
+
"https://learn.topolo.dev",
|
|
4919
|
+
"https://localize.topolo.dev",
|
|
4920
|
+
"https://mail.topolo.dev",
|
|
4921
|
+
"https://mcp.topolo.dev",
|
|
4922
|
+
"https://mdm.topolo.dev",
|
|
4923
|
+
"https://messages.topolo.dev",
|
|
4924
|
+
"https://nexus.topolo.dev",
|
|
4925
|
+
"https://notify.topolo.dev",
|
|
4926
|
+
"https://nurture.topolo.dev",
|
|
4927
|
+
"https://observability-watch.topolo.dev",
|
|
4928
|
+
"https://p2p.topolo.dev",
|
|
4929
|
+
"https://pay.topolo.dev",
|
|
4930
|
+
"https://people.topolo.dev",
|
|
4931
|
+
"https://pulse.topolo.dev",
|
|
4932
|
+
"https://recruit.topolo.dev",
|
|
4933
|
+
"https://render.topolo.dev",
|
|
4934
|
+
"https://roadmap.topolo.dev",
|
|
4935
|
+
"https://seed.topolo.dev",
|
|
4936
|
+
"https://sign.topolo.dev",
|
|
4937
|
+
"https://socialize-api.topolo.dev",
|
|
4938
|
+
"https://spaces.topolo.dev",
|
|
4939
|
+
"https://speech.topolo.dev",
|
|
4940
|
+
"https://status.topolo.dev",
|
|
4941
|
+
"https://studio-api.topolo.dev",
|
|
4942
|
+
"https://studio.topolo.dev",
|
|
4943
|
+
"https://success.topolo.dev",
|
|
4944
|
+
"https://support.topolo.dev",
|
|
4945
|
+
"https://survey.topolo.dev",
|
|
4946
|
+
"https://voice.topolo.dev",
|
|
4947
|
+
"https://web.topolo.dev"
|
|
4948
|
+
])
|
|
4949
|
+
});
|
|
4950
|
+
var TOPOLO_REFUSED_HOST_SUFFIXES2 = Object.freeze(["sites.stg.topolo.us", "sites.topolo.app", "sites.topolo.dev", "topolo.site", "try.topolo.io", "workers.dev"]);
|
|
4951
|
+
var TOPOLO_RUNTIME_ENVIRONMENTS2 = ["production", "staging", "development", "local"];
|
|
4952
|
+
var TOPOLO_ENVIRONMENT_SERVICE_URLS2 = Object.freeze({
|
|
4953
|
+
production: Object.freeze({
|
|
4392
4954
|
auth: "https://auth.topolo.app",
|
|
4393
4955
|
developers: "https://developers.topolo.app"
|
|
4394
|
-
},
|
|
4395
|
-
staging: {
|
|
4956
|
+
}),
|
|
4957
|
+
staging: Object.freeze({
|
|
4396
4958
|
auth: "https://auth.stg.topolo.us",
|
|
4397
4959
|
developers: "https://developers.stg.topolo.us"
|
|
4398
|
-
},
|
|
4399
|
-
development: {
|
|
4960
|
+
}),
|
|
4961
|
+
development: Object.freeze({
|
|
4400
4962
|
auth: "https://auth.topolo.dev",
|
|
4401
4963
|
developers: "https://developers.topolo.dev"
|
|
4402
|
-
},
|
|
4403
|
-
local: {}
|
|
4404
|
-
};
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4964
|
+
}),
|
|
4965
|
+
local: Object.freeze({})
|
|
4966
|
+
});
|
|
4967
|
+
var FIRST_PARTY_ORIGIN_SETS2 = Object.freeze({
|
|
4968
|
+
production: new Set(TOPOLO_FIRST_PARTY_ORIGINS2.production),
|
|
4969
|
+
staging: new Set(TOPOLO_FIRST_PARTY_ORIGINS2.staging),
|
|
4970
|
+
development: new Set(TOPOLO_FIRST_PARTY_ORIGINS2.development)
|
|
4971
|
+
});
|
|
4972
|
+
var LOOPBACK_HOSTNAMES2 = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
4973
|
+
function isRuntimeEnvironment2(value) {
|
|
4974
|
+
return typeof value === "string" && TOPOLO_RUNTIME_ENVIRONMENTS2.includes(value);
|
|
4412
4975
|
}
|
|
4413
|
-
function
|
|
4414
|
-
const hostname = url.hostname.toLowerCase();
|
|
4976
|
+
function originIsFirstParty2(url, environment) {
|
|
4415
4977
|
if (environment === "local") {
|
|
4416
|
-
return
|
|
4978
|
+
return (url.protocol === "http:" || url.protocol === "https:") && LOOPBACK_HOSTNAMES2.has(url.hostname);
|
|
4417
4979
|
}
|
|
4418
4980
|
if (url.protocol !== "https:") return false;
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
if (environment
|
|
4423
|
-
|
|
4981
|
+
return FIRST_PARTY_ORIGIN_SETS2[environment].has(url.origin);
|
|
4982
|
+
}
|
|
4983
|
+
function requireTopoloRuntimeEnvironment2(environment) {
|
|
4984
|
+
if (!isRuntimeEnvironment2(environment)) {
|
|
4985
|
+
throw new TopoloSdkError2(
|
|
4986
|
+
"environment_required",
|
|
4987
|
+
'Refusing to send a Topolo credential: the runtime environment is not resolved. Pass environment: "production" | "staging" | "development" | "local".'
|
|
4988
|
+
);
|
|
4424
4989
|
}
|
|
4425
|
-
return
|
|
4990
|
+
return environment;
|
|
4426
4991
|
}
|
|
4427
4992
|
function assertTopoloServiceUrlEnvironment2(service, value, environment) {
|
|
4993
|
+
const resolved = requireTopoloRuntimeEnvironment2(environment);
|
|
4428
4994
|
let url;
|
|
4429
4995
|
try {
|
|
4430
4996
|
url = new URL(value);
|
|
4431
4997
|
} catch {
|
|
4432
4998
|
throw new TopoloSdkError2("invalid_service_url", `Invalid URL configured for Topolo service "${service}".`);
|
|
4433
4999
|
}
|
|
4434
|
-
if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !
|
|
5000
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !originIsFirstParty2(url, resolved)) {
|
|
4435
5001
|
throw new TopoloSdkError2(
|
|
4436
5002
|
"environment_mismatch",
|
|
4437
|
-
`Refusing Topolo service "${service}": ${url.origin}
|
|
5003
|
+
`Refusing Topolo service "${service}": ${url.origin} is not a first-party ${resolved} service origin.`
|
|
4438
5004
|
);
|
|
4439
5005
|
}
|
|
4440
5006
|
return url.origin;
|
|
@@ -4447,14 +5013,58 @@ function topoloServiceOrigins2(serviceUrls, environment) {
|
|
|
4447
5013
|
])
|
|
4448
5014
|
);
|
|
4449
5015
|
}
|
|
5016
|
+
var REDIRECT_STATUSES2 = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
5017
|
+
async function fetchWithCredentialBoundary2(fetchImpl, environment, service, target, init) {
|
|
5018
|
+
const url = new URL(String(target));
|
|
5019
|
+
const origin = assertTopoloServiceUrlEnvironment2(service, url.toString(), environment);
|
|
5020
|
+
const response = await fetchImpl(url.toString(), { ...init, redirect: "manual" });
|
|
5021
|
+
let responseOrigin = origin;
|
|
5022
|
+
try {
|
|
5023
|
+
if (response.url) responseOrigin = new URL(response.url).origin;
|
|
5024
|
+
} catch {
|
|
5025
|
+
responseOrigin = "";
|
|
5026
|
+
}
|
|
5027
|
+
if (response.redirected === true || responseOrigin !== origin) {
|
|
5028
|
+
await response.body?.cancel().catch(() => void 0);
|
|
5029
|
+
throw new TopoloSdkError2(
|
|
5030
|
+
"credential_redirect_refused",
|
|
5031
|
+
`Refusing a redirected response from Topolo service "${service}"; credentials are never forwarded.`
|
|
5032
|
+
);
|
|
5033
|
+
}
|
|
5034
|
+
if (REDIRECT_STATUSES2.has(response.status)) {
|
|
5035
|
+
const location = response.headers.get("Location");
|
|
5036
|
+
let destination = null;
|
|
5037
|
+
if (location !== null) {
|
|
5038
|
+
try {
|
|
5039
|
+
destination = new URL(location, url).origin;
|
|
5040
|
+
} catch {
|
|
5041
|
+
destination = null;
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
if (location !== null && destination !== origin) {
|
|
5045
|
+
await response.body?.cancel().catch(() => void 0);
|
|
5046
|
+
throw new TopoloSdkError2(
|
|
5047
|
+
"credential_redirect_refused",
|
|
5048
|
+
`Refusing a redirect from Topolo service "${service}" to another origin; credentials are never forwarded.`
|
|
5049
|
+
);
|
|
5050
|
+
}
|
|
5051
|
+
}
|
|
5052
|
+
return response;
|
|
5053
|
+
}
|
|
4450
5054
|
var APP_API_URLS2 = {
|
|
4451
5055
|
auth: "https://auth.topolo.app",
|
|
4452
5056
|
developers: "https://developers.topolo.app"
|
|
4453
5057
|
};
|
|
4454
5058
|
var DEFAULT_APP_URLS2 = APP_API_URLS2;
|
|
4455
|
-
function resolveServiceUrl2(service, overrides) {
|
|
5059
|
+
function resolveServiceUrl2(service, overrides, environment) {
|
|
4456
5060
|
const override = resolveServiceUrlOverride2([service], overrides);
|
|
4457
5061
|
if (override) return override;
|
|
5062
|
+
if (environment !== void 0) {
|
|
5063
|
+
const selected = requireTopoloRuntimeEnvironment2(environment);
|
|
5064
|
+
const origin = TOPOLO_ENVIRONMENT_SERVICE_URLS2[selected][service];
|
|
5065
|
+
if (!origin) throw new TopoloSdkError2("service_url_required", `Configure an explicit ${service} origin for ${selected}.`);
|
|
5066
|
+
return origin;
|
|
5067
|
+
}
|
|
4458
5068
|
return DEFAULT_APP_URLS2[service];
|
|
4459
5069
|
}
|
|
4460
5070
|
function resolveServiceUrlOverride2(keys, overrides) {
|
|
@@ -4476,9 +5086,11 @@ var DEVICE_GRANT2 = "urn:ietf:params:oauth:grant-type:device_code";
|
|
|
4476
5086
|
var TopoloOAuth2 = class {
|
|
4477
5087
|
baseUrl;
|
|
4478
5088
|
fetchImpl;
|
|
5089
|
+
environment;
|
|
4479
5090
|
constructor(options = {}) {
|
|
4480
|
-
this.baseUrl =
|
|
5091
|
+
this.baseUrl = resolveServiceUrl2("auth", options.serviceUrls, options.environment);
|
|
4481
5092
|
this.fetchImpl = options.fetch ?? fetch;
|
|
5093
|
+
this.environment = options.environment;
|
|
4482
5094
|
}
|
|
4483
5095
|
/**
|
|
4484
5096
|
* RFC 8628 device authorization grant — starts the flow by asking the server
|
|
@@ -4543,7 +5155,7 @@ var TopoloOAuth2 = class {
|
|
|
4543
5155
|
}
|
|
4544
5156
|
async #postForm(path, body) {
|
|
4545
5157
|
const url = new URL(path, this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`);
|
|
4546
|
-
const res = await this.fetchImpl
|
|
5158
|
+
const res = await fetchWithCredentialBoundary2(this.fetchImpl, this.environment, "auth", url, {
|
|
4547
5159
|
method: "POST",
|
|
4548
5160
|
headers: {
|
|
4549
5161
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
@@ -4823,11 +5435,11 @@ function createProtectedActionSecretStore(options) {
|
|
|
4823
5435
|
}
|
|
4824
5436
|
};
|
|
4825
5437
|
}
|
|
4826
|
-
var TOPOLO_ENVIRONMENTS =
|
|
5438
|
+
var TOPOLO_ENVIRONMENTS = TOPOLO_RUNTIME_ENVIRONMENTS2;
|
|
4827
5439
|
function contextKey(contextType, organizationId) {
|
|
4828
5440
|
return contextType === "organization" && organizationId ? `org:${organizationId}` : "personal";
|
|
4829
5441
|
}
|
|
4830
|
-
var ENVIRONMENT_DEFAULT_SERVICE_URLS =
|
|
5442
|
+
var ENVIRONMENT_DEFAULT_SERVICE_URLS = TOPOLO_ENVIRONMENT_SERVICE_URLS2;
|
|
4831
5443
|
function configPath() {
|
|
4832
5444
|
const override = process.env.TOPOLO_CONFIG_PATH;
|
|
4833
5445
|
if (override) return override;
|
|
@@ -5155,7 +5767,7 @@ async function signOut(environment, options = {}) {
|
|
|
5155
5767
|
if (options.revoke === false || !grants.length) return result;
|
|
5156
5768
|
let oauth;
|
|
5157
5769
|
try {
|
|
5158
|
-
oauth = boundedOAuth(authServiceUrls(profile, environment, "sign-out"));
|
|
5770
|
+
oauth = boundedOAuth(authServiceUrls(profile, environment, "sign-out"), environment);
|
|
5159
5771
|
} catch {
|
|
5160
5772
|
result.revocationFailed = grants.length;
|
|
5161
5773
|
return result;
|
|
@@ -5203,14 +5815,14 @@ function configLockTimeoutMs() {
|
|
|
5203
5815
|
const value = Number(process.env.TOPOLO_CONFIG_LOCK_TIMEOUT_MS);
|
|
5204
5816
|
return Number.isFinite(value) && value >= 1e3 && value <= 12e4 ? value : CONFIG_LOCK_DEFAULT_TIMEOUT_MS;
|
|
5205
5817
|
}
|
|
5206
|
-
function boundedOAuth(serviceUrls) {
|
|
5818
|
+
function boundedOAuth(serviceUrls, environment) {
|
|
5207
5819
|
const fetchImpl = (input2, init) => globalThis.fetch(input2, {
|
|
5208
5820
|
...init,
|
|
5209
5821
|
// Never re-POST a refresh token, device code or revoked token to another origin.
|
|
5210
5822
|
redirect: "error",
|
|
5211
5823
|
signal: init?.signal ?? AbortSignal.timeout(AUTH_EXCHANGE_TIMEOUT_MS)
|
|
5212
5824
|
});
|
|
5213
|
-
return new TopoloOAuth2({ serviceUrls, fetch: fetchImpl });
|
|
5825
|
+
return new TopoloOAuth2({ serviceUrls, fetch: fetchImpl, environment });
|
|
5214
5826
|
}
|
|
5215
5827
|
var CONFIG_LOCK_DEAD_OWNER_MIN_AGE_MS = 2e3;
|
|
5216
5828
|
var ConfigOwnershipError = class extends TopoloSdkError2 {
|
|
@@ -5827,7 +6439,7 @@ async function renewTarget(environment, target, options) {
|
|
|
5827
6439
|
return endGrant(retired.reason === "rotated" ? "rotated_grant_lost" : retired.reason);
|
|
5828
6440
|
}
|
|
5829
6441
|
const serviceUrls = authServiceUrls(profile, environment, target.kind === "active" ? "credential refresh" : "context refresh");
|
|
5830
|
-
const oauth = boundedOAuth(serviceUrls);
|
|
6442
|
+
const oauth = boundedOAuth(serviceUrls, environment);
|
|
5831
6443
|
try {
|
|
5832
6444
|
await persist((current) => {
|
|
5833
6445
|
const next = clone(current);
|
|
@@ -5922,7 +6534,7 @@ async function continueReauthorization(environment, key) {
|
|
|
5922
6534
|
const clientId = (key === DEFAULT_SLOT ? void 0 : profile.contexts?.[key]?.oauthClientId) ?? profile.oauthClientId;
|
|
5923
6535
|
if (!clientId) throw reauthRequired(environment, key, entry, "unavailable");
|
|
5924
6536
|
const serviceUrls = authServiceUrls(profile, environment, "sign-in renewal");
|
|
5925
|
-
const oauth = boundedOAuth(serviceUrls);
|
|
6537
|
+
const oauth = boundedOAuth(serviceUrls, environment);
|
|
5926
6538
|
const now = nowSeconds();
|
|
5927
6539
|
let state = clone(entry);
|
|
5928
6540
|
const save = async (update) => {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/cli",
|
|
3
3
|
"packageManager": "pnpm@10.17.1",
|
|
4
|
-
"version": "0.13.
|
|
5
|
-
"topoloPublishedVersion": "0.13.
|
|
4
|
+
"version": "0.13.27",
|
|
5
|
+
"topoloPublishedVersion": "0.13.27",
|
|
6
6
|
"description": "Public command-line interface for Topolo customer organizations, developers, and agents.",
|
|
7
7
|
"license": "UNLICENSED",
|
|
8
8
|
"author": "Topolo",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@modelcontextprotocol/sdk": "1.31.0",
|
|
36
|
-
"@topolo/mcp": "0.14.
|
|
37
|
-
"@topolo/sdk": "0.
|
|
36
|
+
"@topolo/mcp": "0.14.39",
|
|
37
|
+
"@topolo/sdk": "0.24.1",
|
|
38
38
|
"commander": "^12.1.0",
|
|
39
39
|
"undici": "6.29.0"
|
|
40
40
|
},
|