@topolo/cli 0.13.26 → 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.
Files changed (2) hide show
  1. package/dist/index.js +321 -233
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -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,152 +755,6 @@ var TopoloReauthRequiredError = class extends TopoloAuthError {
823
755
  this.reauthorization = Object.freeze({ ...reauthorization });
824
756
  }
825
757
  };
826
- var ACTION_SECRET_PREFIX = "topolo-secret:";
827
- function isActionSecretReference(value) {
828
- return typeof value === "string" && value.startsWith(ACTION_SECRET_PREFIX);
829
- }
830
- function isRecord(value) {
831
- return typeof value === "object" && value !== null && !Array.isArray(value);
832
- }
833
- function sensitive(key, value) {
834
- const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
835
- if (normalized === "capability") {
836
- if (typeof value !== "string") return value !== null && value !== void 0 && typeof value !== "boolean" && typeof value !== "number";
837
- const text4 = value.trim();
838
- return Boolean(text4) && !/^[a-z][a-z0-9_-]{0,31}$/.test(text4);
839
- }
840
- return ["authorization", "cookie", "setcookie", "proxyauthorization"].includes(normalized) || /(token|secret|password|privatekey|apikey|backupcodes?|recoverycodes?)$/.test(normalized);
841
- }
842
- function maskActionSecrets(value, secrets) {
843
- let masked = value;
844
- for (const [secret, reference] of secrets) {
845
- const encoded = encodeURIComponent(secret);
846
- const variants = [.../* @__PURE__ */ new Set([secret, encoded])].map((item) => item.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
847
- masked = masked.replace(new RegExp(variants.join("|"), "g"), (match) => match === secret ? reference : encodeURIComponent(reference));
848
- }
849
- return masked;
850
- }
851
- async function resolveActionSecrets(value, store, secrets) {
852
- if (isActionSecretReference(value)) {
853
- if (!store) throw new TopoloSdkError("action_secret_store_required", "Protected action references require a secret store.");
854
- const resolved = await store.load(value);
855
- if (typeof resolved !== "string" || !resolved) throw new TopoloSdkError("action_secret_unsupported_type", "Protected action handoff requires a nonempty string credential.");
856
- secrets.set(resolved, value);
857
- return resolved;
858
- }
859
- if (Array.isArray(value)) return Promise.all(value.map((item) => resolveActionSecrets(item, store, secrets)));
860
- if (!isRecord(value)) return value;
861
- return Object.fromEntries(await Promise.all(Object.entries(value).map(async ([key, item]) => [key, await resolveActionSecrets(item, store, secrets)])));
862
- }
863
- async function protectActionResult(value, store, options = {}) {
864
- const secrets = /* @__PURE__ */ new Map();
865
- function documentation(item, key) {
866
- if (options.preserveActionSchemas === true && ["inputSchema", "outputSchema", "input_schema", "output_schema"].includes(key) && isRecord(item)) return true;
867
- if (options.preserveActionContracts === true && ["agentContract", "agent_contract"].includes(key) && isRecord(item)) return true;
868
- return options.preserveAgentGuidance === true && key === "agentGuidance" && isRecord(item) && item.schemaVersion === 1 && typeof item.kind === "string" && ["action-guide", "product-guide", "concept"].includes(item.kind);
869
- }
870
- function localizationMap(path) {
871
- const location = path.join("/");
872
- if (options.localizationAction === "sources.verify") return location === "data/hashes";
873
- if (options.localizationAction === "values.bulk_upsert") return location === "data/sourceHashes";
874
- return ["translation.release.prepare", "translation.release.import", "translation.release.get"].includes(options.localizationAction ?? "") && /^data\/bundle\/catalogs\/\*\/(sources|(?:packs|provenance)\/[^/]+)$/.test(location);
875
- }
876
- async function protect(item, key = "", path = []) {
877
- if (documentation(item, key)) return item;
878
- if (isActionSecretReference(item)) return item;
879
- if (Array.isArray(item) && /(?:backup|recovery)codes$/.test(key.toLowerCase().replace(/[^a-z0-9]/g, ""))) {
880
- return Promise.all(item.map((child) => protect(child, key, [...path, "*"])));
881
- }
882
- if (sensitive(key, item)) {
883
- const reference = store ? await store.save(item) : "[REDACTED]";
884
- if (typeof item === "string" && item) secrets.set(item, reference);
885
- return reference;
886
- }
887
- if (Array.isArray(item)) return Promise.all(item.map((child) => protect(child, "", [...path, "*"])));
888
- if (!isRecord(item)) return item;
889
- return Object.fromEntries(await Promise.all(Object.entries(item).map(async ([name, child]) => [name, await protect(child, localizationMap(path) ? "" : name, [...path, name])])));
890
- }
891
- const protectedValue = await protect(value);
892
- async function protectEmbedded(item, key = "") {
893
- if (documentation(item, key)) return item;
894
- if (typeof item === "string" && !isActionSecretReference(item) && maskActionSecrets(item, secrets) !== item) {
895
- return store ? store.save(item) : maskActionSecrets(item, secrets);
896
- }
897
- if (Array.isArray(item)) return Promise.all(item.map((child) => protectEmbedded(child)));
898
- if (!isRecord(item)) return item;
899
- return Object.fromEntries(await Promise.all(Object.entries(item).map(async ([key2, child]) => [key2, await protectEmbedded(child, key2)])));
900
- }
901
- return protectEmbedded(protectedValue);
902
- }
903
- function maskActionSecretValue(value, secrets) {
904
- if (value instanceof Error) return maskActionSecretError(value, secrets);
905
- if (typeof value === "string") return maskActionSecrets(value, secrets);
906
- if (Array.isArray(value)) return value.map((item) => maskActionSecretValue(item, secrets));
907
- if (!isRecord(value)) return value;
908
- return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, maskActionSecretValue(item, secrets)]));
909
- }
910
- function maskActionSecretError(error, secrets) {
911
- if (!(error instanceof Error)) return maskActionSecretValue(error, secrets);
912
- const masked = Object.create(Object.getPrototypeOf(error));
913
- for (const key of Object.getOwnPropertyNames(error)) {
914
- const descriptor = Object.getOwnPropertyDescriptor(error, key);
915
- if ("value" in descriptor) descriptor.value = maskActionSecretValue(descriptor.value, secrets);
916
- Object.defineProperty(masked, key, descriptor);
917
- }
918
- return masked;
919
- }
920
- var encoder = new TextEncoder();
921
- function encodeBase64Url(value) {
922
- const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);
923
- let binary = "";
924
- for (const byte of bytes) binary += String.fromCharCode(byte);
925
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
926
- }
927
- function encodeJson(value) {
928
- return encodeBase64Url(encoder.encode(JSON.stringify(value)));
929
- }
930
- async function sha256Base64Url(value) {
931
- return encodeBase64Url(await crypto.subtle.digest("SHA-256", encoder.encode(value)));
932
- }
933
- function publicJwk(privateJwk) {
934
- const { crv, kty, x, y } = privateJwk;
935
- if (kty !== "EC" || crv !== "P-256" || !x || !y || !privateJwk.d) {
936
- throw new TypeError("A private P-256 JSON Web Key is required for DPoP signing.");
937
- }
938
- return { crv, kty, x, y };
939
- }
940
- function canonicalTargetUrl(value) {
941
- const url = new URL(value);
942
- return `${url.origin}${url.pathname}`;
943
- }
944
- async function calculateDpopJkt(privateJwk) {
945
- const jwk = publicJwk(privateJwk);
946
- return sha256Base64Url(JSON.stringify({ crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y }));
947
- }
948
- async function createDpopProof(input2) {
949
- const jwk = publicJwk(input2.privateJwk);
950
- const key = await crypto.subtle.importKey(
951
- "jwk",
952
- input2.privateJwk,
953
- { name: "ECDSA", namedCurve: "P-256" },
954
- false,
955
- ["sign"]
956
- );
957
- const header = encodeJson({ alg: "ES256", typ: "dpop+jwt", jwk });
958
- const claims = encodeJson({
959
- jti: crypto.randomUUID(),
960
- iat: Math.floor(Date.now() / 1e3),
961
- htm: input2.method.toUpperCase(),
962
- htu: canonicalTargetUrl(input2.targetUrl),
963
- ath: await sha256Base64Url(input2.accessToken)
964
- });
965
- const signature = await crypto.subtle.sign(
966
- { name: "ECDSA", hash: "SHA-256" },
967
- key,
968
- encoder.encode(`${header}.${claims}`)
969
- );
970
- return `${header}.${claims}.${encodeBase64Url(signature)}`;
971
- }
972
758
  var TOPOLO_FIRST_PARTY_ORIGINS = Object.freeze({
973
759
  production: Object.freeze([
974
760
  "https://admin.topolo.app",
@@ -1269,6 +1055,270 @@ async function fetchWithCredentialBoundary(fetchImpl, environment, service, targ
1269
1055
  }
1270
1056
  return response;
1271
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
+ }
1176
+ var ACTION_SECRET_PREFIX = "topolo-secret:";
1177
+ function isActionSecretReference(value) {
1178
+ return typeof value === "string" && value.startsWith(ACTION_SECRET_PREFIX);
1179
+ }
1180
+ function isRecord(value) {
1181
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1182
+ }
1183
+ function sensitive(key, value) {
1184
+ const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
1185
+ if (normalized === "capability") {
1186
+ if (typeof value !== "string") return value !== null && value !== void 0 && typeof value !== "boolean" && typeof value !== "number";
1187
+ const text4 = value.trim();
1188
+ return Boolean(text4) && !/^[a-z][a-z0-9_-]{0,31}$/.test(text4);
1189
+ }
1190
+ return ["authorization", "cookie", "setcookie", "proxyauthorization"].includes(normalized) || /(token|secret|password|privatekey|apikey|backupcodes?|recoverycodes?)$/.test(normalized);
1191
+ }
1192
+ function maskActionSecrets(value, secrets) {
1193
+ let masked = value;
1194
+ for (const [secret, reference] of secrets) {
1195
+ const encoded = encodeURIComponent(secret);
1196
+ const variants = [.../* @__PURE__ */ new Set([secret, encoded])].map((item) => item.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
1197
+ masked = masked.replace(new RegExp(variants.join("|"), "g"), (match) => match === secret ? reference : encodeURIComponent(reference));
1198
+ }
1199
+ return masked;
1200
+ }
1201
+ async function resolveActionSecrets(value, store, secrets) {
1202
+ if (isActionSecretReference(value)) {
1203
+ if (!store) throw new TopoloSdkError("action_secret_store_required", "Protected action references require a secret store.");
1204
+ const resolved = await store.load(value);
1205
+ if (typeof resolved !== "string" || !resolved) throw new TopoloSdkError("action_secret_unsupported_type", "Protected action handoff requires a nonempty string credential.");
1206
+ secrets.set(resolved, value);
1207
+ return resolved;
1208
+ }
1209
+ if (Array.isArray(value)) return Promise.all(value.map((item) => resolveActionSecrets(item, store, secrets)));
1210
+ if (!isRecord(value)) return value;
1211
+ return Object.fromEntries(await Promise.all(Object.entries(value).map(async ([key, item]) => [key, await resolveActionSecrets(item, store, secrets)])));
1212
+ }
1213
+ async function protectActionResult(value, store, options = {}) {
1214
+ const secrets = /* @__PURE__ */ new Map();
1215
+ function documentation(item, key) {
1216
+ if (options.preserveActionSchemas === true && ["inputSchema", "outputSchema", "input_schema", "output_schema"].includes(key) && isRecord(item)) return true;
1217
+ if (options.preserveActionContracts === true && ["agentContract", "agent_contract"].includes(key) && isRecord(item)) return true;
1218
+ return options.preserveAgentGuidance === true && key === "agentGuidance" && isRecord(item) && item.schemaVersion === 1 && typeof item.kind === "string" && ["action-guide", "product-guide", "concept"].includes(item.kind);
1219
+ }
1220
+ function localizationMap(path) {
1221
+ const location = path.join("/");
1222
+ if (options.localizationAction === "sources.verify") return location === "data/hashes";
1223
+ if (options.localizationAction === "values.bulk_upsert") return location === "data/sourceHashes";
1224
+ return ["translation.release.prepare", "translation.release.import", "translation.release.get"].includes(options.localizationAction ?? "") && /^data\/bundle\/catalogs\/\*\/(sources|(?:packs|provenance)\/[^/]+)$/.test(location);
1225
+ }
1226
+ async function protect(item, key = "", path = []) {
1227
+ if (documentation(item, key)) return item;
1228
+ if (isActionSecretReference(item)) return item;
1229
+ if (Array.isArray(item) && /(?:backup|recovery)codes$/.test(key.toLowerCase().replace(/[^a-z0-9]/g, ""))) {
1230
+ return Promise.all(item.map((child) => protect(child, key, [...path, "*"])));
1231
+ }
1232
+ if (sensitive(key, item)) {
1233
+ const reference = store ? await store.save(item) : "[REDACTED]";
1234
+ if (typeof item === "string" && item) secrets.set(item, reference);
1235
+ return reference;
1236
+ }
1237
+ if (Array.isArray(item)) return Promise.all(item.map((child) => protect(child, "", [...path, "*"])));
1238
+ if (!isRecord(item)) return item;
1239
+ return Object.fromEntries(await Promise.all(Object.entries(item).map(async ([name, child]) => [name, await protect(child, localizationMap(path) ? "" : name, [...path, name])])));
1240
+ }
1241
+ const protectedValue = await protect(value);
1242
+ async function protectEmbedded(item, key = "") {
1243
+ if (documentation(item, key)) return item;
1244
+ if (typeof item === "string" && !isActionSecretReference(item) && maskActionSecrets(item, secrets) !== item) {
1245
+ return store ? store.save(item) : maskActionSecrets(item, secrets);
1246
+ }
1247
+ if (Array.isArray(item)) return Promise.all(item.map((child) => protectEmbedded(child)));
1248
+ if (!isRecord(item)) return item;
1249
+ return Object.fromEntries(await Promise.all(Object.entries(item).map(async ([key2, child]) => [key2, await protectEmbedded(child, key2)])));
1250
+ }
1251
+ return protectEmbedded(protectedValue);
1252
+ }
1253
+ function maskActionSecretValue(value, secrets) {
1254
+ if (value instanceof Error) return maskActionSecretError(value, secrets);
1255
+ if (typeof value === "string") return maskActionSecrets(value, secrets);
1256
+ if (Array.isArray(value)) return value.map((item) => maskActionSecretValue(item, secrets));
1257
+ if (!isRecord(value)) return value;
1258
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, maskActionSecretValue(item, secrets)]));
1259
+ }
1260
+ function maskActionSecretError(error, secrets) {
1261
+ if (!(error instanceof Error)) return maskActionSecretValue(error, secrets);
1262
+ const masked = Object.create(Object.getPrototypeOf(error));
1263
+ for (const key of Object.getOwnPropertyNames(error)) {
1264
+ const descriptor = Object.getOwnPropertyDescriptor(error, key);
1265
+ if ("value" in descriptor) descriptor.value = maskActionSecretValue(descriptor.value, secrets);
1266
+ Object.defineProperty(masked, key, descriptor);
1267
+ }
1268
+ return masked;
1269
+ }
1270
+ var encoder = new TextEncoder();
1271
+ function encodeBase64Url(value) {
1272
+ const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);
1273
+ let binary = "";
1274
+ for (const byte of bytes) binary += String.fromCharCode(byte);
1275
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
1276
+ }
1277
+ function encodeJson(value) {
1278
+ return encodeBase64Url(encoder.encode(JSON.stringify(value)));
1279
+ }
1280
+ async function sha256Base64Url(value) {
1281
+ return encodeBase64Url(await crypto.subtle.digest("SHA-256", encoder.encode(value)));
1282
+ }
1283
+ function publicJwk(privateJwk) {
1284
+ const { crv, kty, x, y } = privateJwk;
1285
+ if (kty !== "EC" || crv !== "P-256" || !x || !y || !privateJwk.d) {
1286
+ throw new TypeError("A private P-256 JSON Web Key is required for DPoP signing.");
1287
+ }
1288
+ return { crv, kty, x, y };
1289
+ }
1290
+ function canonicalTargetUrl(value) {
1291
+ const url = new URL(value);
1292
+ return `${url.origin}${url.pathname}`;
1293
+ }
1294
+ async function calculateDpopJkt(privateJwk) {
1295
+ const jwk = publicJwk(privateJwk);
1296
+ return sha256Base64Url(JSON.stringify({ crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y }));
1297
+ }
1298
+ async function createDpopProof(input2) {
1299
+ const jwk = publicJwk(input2.privateJwk);
1300
+ const key = await crypto.subtle.importKey(
1301
+ "jwk",
1302
+ input2.privateJwk,
1303
+ { name: "ECDSA", namedCurve: "P-256" },
1304
+ false,
1305
+ ["sign"]
1306
+ );
1307
+ const header = encodeJson({ alg: "ES256", typ: "dpop+jwt", jwk });
1308
+ const claims = encodeJson({
1309
+ jti: crypto.randomUUID(),
1310
+ iat: Math.floor(Date.now() / 1e3),
1311
+ htm: input2.method.toUpperCase(),
1312
+ htu: canonicalTargetUrl(input2.targetUrl),
1313
+ ath: await sha256Base64Url(input2.accessToken)
1314
+ });
1315
+ const signature = await crypto.subtle.sign(
1316
+ { name: "ECDSA", hash: "SHA-256" },
1317
+ key,
1318
+ encoder.encode(`${header}.${claims}`)
1319
+ );
1320
+ return `${header}.${claims}.${encodeBase64Url(signature)}`;
1321
+ }
1272
1322
  var APP_API_URLS = {
1273
1323
  auth: "https://auth.topolo.app",
1274
1324
  developers: "https://developers.topolo.app"
@@ -2140,7 +2190,17 @@ var TopoloClient = class _TopoloClient {
2140
2190
  * use and for the generic `topolo api` / MCP passthrough tool.
2141
2191
  */
2142
2192
  async request(opts) {
2193
+ return this.requestWithTarget(opts);
2194
+ }
2195
+ async requestWithTarget(opts, publicAction) {
2143
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
+ }
2144
2204
  const method = opts.method ?? "GET";
2145
2205
  if (this.requireConfirmForWrites && WRITE_METHODS.has(method) && !opts.confirm) {
2146
2206
  throw new TopoloConfirmationRequiredError(
@@ -2149,7 +2209,7 @@ var TopoloClient = class _TopoloClient {
2149
2209
  }
2150
2210
  const actionId = requestActionId(opts.headers);
2151
2211
  const taskId = opts.actorEnvelope?.delegation?.taskId ?? this.actorContext?.taskId;
2152
- let requestCredential = await this.loadCurrentCredential({
2212
+ let requestCredential = publicOrigin ? void 0 : await this.loadCurrentCredential({
2153
2213
  service: normalizeLookupKey(opts.service),
2154
2214
  method,
2155
2215
  path: opts.path,
@@ -2157,7 +2217,7 @@ var TopoloClient = class _TopoloClient {
2157
2217
  ...taskId ? { taskId } : {},
2158
2218
  ...opts.resource ? { resource: opts.resource } : {}
2159
2219
  });
2160
- 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);
2161
2221
  const baseUrl = resolvedService.baseUrl;
2162
2222
  const url = new URL(opts.path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
2163
2223
  if (url.origin !== new URL(baseUrl).origin) {
@@ -2178,17 +2238,21 @@ var TopoloClient = class _TopoloClient {
2178
2238
  headers.set("Accept", "application/json");
2179
2239
  if (opts.body !== void 0) headers.set("Content-Type", "application/json");
2180
2240
  const platformAppId = resolveConfiguredAppId(resolvedService.serviceKey, this.appIds);
2181
- if (platformAppId) headers.set("X-App-ID", platformAppId);
2182
- 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);
2183
2243
  const requestId = actorEnvelope?.action.requestId ?? generateRequestId();
2184
- applyAuditHeaders(headers, this.agent, requestId);
2185
- if (opts.headers) {
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) {
2186
2250
  for (const [k, v] of Object.entries(opts.headers)) headers.set(k, v);
2187
2251
  }
2188
2252
  if (actorEnvelope) {
2189
2253
  headers.set(TOPOLO_ACTOR_ENVELOPE_HEADER, encodeActorEnvelopeHeader(actorEnvelope));
2190
2254
  }
2191
- if (opts.resource) {
2255
+ if (opts.resource && !publicOrigin) {
2192
2256
  const resourceType = opts.resource.resourceType.trim();
2193
2257
  const resourceId = opts.resource.resourceId.trim();
2194
2258
  if (!resourceType || !resourceId) {
@@ -2213,8 +2277,8 @@ var TopoloClient = class _TopoloClient {
2213
2277
  const signal = opts.signal ? mergeSignals(opts.signal, controller.signal) : controller.signal;
2214
2278
  try {
2215
2279
  const requestHeaders = new Headers(headers);
2216
- applyAuthHeaders(requestHeaders, requestCredential);
2217
- if (requestCredential.kind === "dpop_access_token") {
2280
+ if (requestCredential) applyAuthHeaders(requestHeaders, requestCredential);
2281
+ if (requestCredential?.kind === "dpop_access_token") {
2218
2282
  requestHeaders.set("DPoP", await createDpopProof({
2219
2283
  accessToken: requestCredential.accessToken,
2220
2284
  method,
@@ -2222,12 +2286,13 @@ var TopoloClient = class _TopoloClient {
2222
2286
  privateJwk: requestCredential.privateJwk
2223
2287
  }));
2224
2288
  }
2225
- return await this.credentialedFetch(resolvedService.serviceKey, urlStr, {
2289
+ const init = {
2226
2290
  method,
2227
2291
  headers: requestHeaders,
2228
2292
  body: opts.rawBody ?? (opts.body !== void 0 ? JSON.stringify(opts.body) : null),
2229
2293
  signal
2230
- });
2294
+ };
2295
+ return publicOrigin ? await fetchPublicMarketingAction((input2, requestInit) => this.fetchImpl(input2, requestInit), publicAction, this.environment, url, init) : await this.credentialedFetch(resolvedService.serviceKey, urlStr, init);
2231
2296
  } catch (error) {
2232
2297
  if (timedOut) {
2233
2298
  throw new TopoloSdkError(
@@ -2235,6 +2300,9 @@ var TopoloClient = class _TopoloClient {
2235
2300
  `Topolo request timed out after ${this.timeoutMs}ms. Verify current state before retrying a mutation.`
2236
2301
  );
2237
2302
  }
2303
+ if (publicOrigin && opts.signal?.aborted) {
2304
+ throw new TopoloSdkError("request_cancelled", "Public action request was cancelled.");
2305
+ }
2238
2306
  throw error;
2239
2307
  } finally {
2240
2308
  clearTimeout(timeoutId);
@@ -2257,7 +2325,7 @@ var TopoloClient = class _TopoloClient {
2257
2325
  try {
2258
2326
  const attemptedCredential = requestCredential;
2259
2327
  res = await fetchWithReadRetry();
2260
- if (res.status === 401 && attemptedCredential.kind === "access_token" && this.refreshCredential) {
2328
+ if (res.status === 401 && attemptedCredential?.kind === "access_token" && this.refreshCredential) {
2261
2329
  await res.body?.cancel().catch(() => void 0);
2262
2330
  const refreshed = await this.refreshRejectedAccessToken(attemptedCredential.accessToken);
2263
2331
  if (refreshed) {
@@ -2411,6 +2479,9 @@ var TopoloClient = class _TopoloClient {
2411
2479
  return fetchWithCredentialBoundary((input2, requestInit) => this.fetchImpl(input2, requestInit), this.environment, service, target, init);
2412
2480
  }
2413
2481
  validatedActionEntry(entry) {
2482
+ if (isOnePublicMarketingAction(entry)) {
2483
+ return { ...entry, apiBaseUrl: publicMarketingOrigin(entry, this.environment) };
2484
+ }
2414
2485
  if (!entry.apiBaseUrl) return entry;
2415
2486
  return {
2416
2487
  ...entry,
@@ -2652,7 +2723,7 @@ var TopoloClient = class _TopoloClient {
2652
2723
  "TopoloAuth did not return an action catalog for the current credential."
2653
2724
  );
2654
2725
  }
2655
- const entries = raw.map((item) => normalizeActionCatalogEntry(item)).filter((entry) => Boolean(entry)).map((entry) => this.validatedActionEntry(entry));
2726
+ const entries = raw.map((item) => normalizeActionCatalogEntry(item)).filter((entry) => Boolean(entry));
2656
2727
  return {
2657
2728
  actions: entries,
2658
2729
  page: normalizeCatalogPage(
@@ -2717,11 +2788,18 @@ var TopoloClient = class _TopoloClient {
2717
2788
  const raw = res.data?.action ?? res.action;
2718
2789
  const match = normalizeActionCatalogEntry(raw);
2719
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
+ }
2720
2794
  return this.validatedActionEntry(match);
2721
2795
  }
2722
2796
  async callAction(action, input2 = {}, options = {}) {
2723
- const entry = await this.getAction(action);
2724
- return this.executeResolvedAction(entry, input2, options);
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);
2725
2803
  }
2726
2804
  async issueExternalAgentCredential(input2) {
2727
2805
  const actionId = "app_topolo_auth.principal_credentials.issue";
@@ -2865,7 +2943,15 @@ var TopoloClient = class _TopoloClient {
2865
2943
  }
2866
2944
  return this.executeResolvedAction(entry, input2, options);
2867
2945
  }
2868
- 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
+ }
2869
2955
  const agentAccess = entry.agentAccess ?? (entry.destructive ? "off" : entry.readOnly && !entry.requiresConfirmation ? "auto" : "confirm");
2870
2956
  if (agentAccess === "off") {
2871
2957
  throw new TopoloPermissionError(
@@ -2894,7 +2980,8 @@ var TopoloClient = class _TopoloClient {
2894
2980
  );
2895
2981
  }
2896
2982
  const mapped = mapActionInput(entry, input2);
2897
- const resolvedActorEnvelope = options.actorEnvelope ?? await this.buildVerifiedAgentActorEnvelope(entry.actionId, input2, options.resource);
2983
+ const publicAction = isOnePublicMarketingAction(entry);
2984
+ const resolvedActorEnvelope = publicAction ? void 0 : options.actorEnvelope ?? await this.buildVerifiedAgentActorEnvelope(entry.actionId, input2, options.resource);
2898
2985
  if (resolvedActorEnvelope) {
2899
2986
  const actorEnvelope = parseActorEnvelope(resolvedActorEnvelope);
2900
2987
  if (actorEnvelope.action.actionId !== entry.actionId) {
@@ -2911,8 +2998,9 @@ var TopoloClient = class _TopoloClient {
2911
2998
  const service = entry.appSlug || entry.appId;
2912
2999
  let result;
2913
3000
  try {
2914
- result = await this.request({
3001
+ result = await this.requestWithTarget({
2915
3002
  service,
3003
+ signal: options.signal,
2916
3004
  ...entry.apiBaseUrl ? { baseUrl: entry.apiBaseUrl } : {},
2917
3005
  method: entry.method,
2918
3006
  path: mapped.path,
@@ -2925,7 +3013,7 @@ var TopoloClient = class _TopoloClient {
2925
3013
  [TOPOLO_ACTION_NAME_HEADER]: entry.name
2926
3014
  },
2927
3015
  confirm: options.confirm === true || entry.readOnly && !entry.destructive && !entry.requiresConfirmation
2928
- });
3016
+ }, publicAction ? entry : void 0);
2929
3017
  } catch (error) {
2930
3018
  throw maskActionSecretError(error, this.actionSecrets);
2931
3019
  }
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.26",
5
- "topoloPublishedVersion": "0.13.26",
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.38",
37
- "@topolo/sdk": "0.24.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
  },