@topolo/mcp 0.11.12 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -15
- package/dist/http.js +893 -695
- package/dist/index.js +1190 -76
- package/package.json +10 -5
package/dist/index.js
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
|
|
6
|
-
// node_modules/@topolo/sdk/dist/index.js
|
|
6
|
+
// node_modules/.pnpm/@topolo+sdk@0.21.0/node_modules/@topolo/sdk/dist/index.js
|
|
7
7
|
function applyAuthHeaders(headers, credential) {
|
|
8
8
|
if (credential.kind === "api_key") {
|
|
9
9
|
headers.set("X-Api-Key", credential.apiKey);
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
|
-
headers.set("Authorization",
|
|
12
|
+
headers.set("Authorization", `${credential.kind === "dpop_access_token" ? "DPoP" : "Bearer"} ${credential.accessToken}`);
|
|
13
13
|
}
|
|
14
14
|
function applyAuditHeaders(headers, agent, requestId) {
|
|
15
15
|
headers.set("X-Topolo-Client", `${agent.clientName}/${agent.clientVersion}`);
|
|
@@ -22,6 +22,54 @@ function generateRequestId() {
|
|
|
22
22
|
}
|
|
23
23
|
return `req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
24
24
|
}
|
|
25
|
+
var encoder = new TextEncoder();
|
|
26
|
+
function encodeBase64Url(value) {
|
|
27
|
+
const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);
|
|
28
|
+
let binary = "";
|
|
29
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
30
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
31
|
+
}
|
|
32
|
+
function encodeJson(value) {
|
|
33
|
+
return encodeBase64Url(encoder.encode(JSON.stringify(value)));
|
|
34
|
+
}
|
|
35
|
+
async function sha256Base64Url(value) {
|
|
36
|
+
return encodeBase64Url(await crypto.subtle.digest("SHA-256", encoder.encode(value)));
|
|
37
|
+
}
|
|
38
|
+
function publicJwk(privateJwk) {
|
|
39
|
+
const { crv, kty, x, y } = privateJwk;
|
|
40
|
+
if (kty !== "EC" || crv !== "P-256" || !x || !y || !privateJwk.d) {
|
|
41
|
+
throw new TypeError("A private P-256 JSON Web Key is required for DPoP signing.");
|
|
42
|
+
}
|
|
43
|
+
return { crv, kty, x, y };
|
|
44
|
+
}
|
|
45
|
+
function canonicalTargetUrl(value) {
|
|
46
|
+
const url = new URL(value);
|
|
47
|
+
return `${url.origin}${url.pathname}`;
|
|
48
|
+
}
|
|
49
|
+
async function createDpopProof(input) {
|
|
50
|
+
const jwk = publicJwk(input.privateJwk);
|
|
51
|
+
const key = await crypto.subtle.importKey(
|
|
52
|
+
"jwk",
|
|
53
|
+
input.privateJwk,
|
|
54
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
55
|
+
false,
|
|
56
|
+
["sign"]
|
|
57
|
+
);
|
|
58
|
+
const header = encodeJson({ alg: "ES256", typ: "dpop+jwt", jwk });
|
|
59
|
+
const claims = encodeJson({
|
|
60
|
+
jti: crypto.randomUUID(),
|
|
61
|
+
iat: Math.floor(Date.now() / 1e3),
|
|
62
|
+
htm: input.method.toUpperCase(),
|
|
63
|
+
htu: canonicalTargetUrl(input.targetUrl),
|
|
64
|
+
ath: await sha256Base64Url(input.accessToken)
|
|
65
|
+
});
|
|
66
|
+
const signature = await crypto.subtle.sign(
|
|
67
|
+
{ name: "ECDSA", hash: "SHA-256" },
|
|
68
|
+
key,
|
|
69
|
+
encoder.encode(`${header}.${claims}`)
|
|
70
|
+
);
|
|
71
|
+
return `${header}.${claims}.${encodeBase64Url(signature)}`;
|
|
72
|
+
}
|
|
25
73
|
var TopoloSdkError = class extends Error {
|
|
26
74
|
code;
|
|
27
75
|
constructor(code, message) {
|
|
@@ -167,10 +215,10 @@ var TopoloPermissionError = class extends TopoloSdkError {
|
|
|
167
215
|
};
|
|
168
216
|
function extractTopoloServiceErrorCode(body) {
|
|
169
217
|
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
170
|
-
const
|
|
171
|
-
const topLevel = canonicalTopoloErrorCode(
|
|
218
|
+
const record2 = body;
|
|
219
|
+
const topLevel = canonicalTopoloErrorCode(record2.code);
|
|
172
220
|
if (topLevel) return topLevel;
|
|
173
|
-
const nested =
|
|
221
|
+
const nested = record2.error;
|
|
174
222
|
const nestedString = canonicalTopoloErrorCode(nested);
|
|
175
223
|
if (nestedString) return nestedString;
|
|
176
224
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
@@ -185,14 +233,14 @@ function serviceErrorCode(body, fallback) {
|
|
|
185
233
|
}
|
|
186
234
|
function extractTopoloServiceErrorDetails(body) {
|
|
187
235
|
if (!body || typeof body !== "object" || Array.isArray(body)) return void 0;
|
|
188
|
-
const
|
|
189
|
-
let candidate =
|
|
190
|
-
const nested =
|
|
236
|
+
const record2 = body;
|
|
237
|
+
let candidate = record2.details;
|
|
238
|
+
const nested = record2.error;
|
|
191
239
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
192
240
|
const nestedRecord = nested;
|
|
193
241
|
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
194
242
|
}
|
|
195
|
-
if (candidate === void 0) candidate =
|
|
243
|
+
if (candidate === void 0) candidate = record2;
|
|
196
244
|
if (Array.isArray(candidate)) {
|
|
197
245
|
const paths = canonicalTopoloValidationPaths(candidate);
|
|
198
246
|
return paths.length ? { paths } : void 0;
|
|
@@ -244,8 +292,8 @@ function canonicalTopoloValidationPaths(issues) {
|
|
|
244
292
|
const paths = [];
|
|
245
293
|
for (const issue of issues) {
|
|
246
294
|
if (!issue || typeof issue !== "object" || Array.isArray(issue)) continue;
|
|
247
|
-
const
|
|
248
|
-
const path = canonicalTopoloValidationPath(
|
|
295
|
+
const record2 = issue;
|
|
296
|
+
const path = canonicalTopoloValidationPath(record2.field) ?? canonicalTopoloValidationPath(record2.path);
|
|
249
297
|
if (path && !paths.includes(path)) paths.push(path);
|
|
250
298
|
if (paths.length === 32) break;
|
|
251
299
|
}
|
|
@@ -712,6 +760,222 @@ function isMultipleOf(value, divisor) {
|
|
|
712
760
|
const quotient = value / divisor;
|
|
713
761
|
return Math.abs(quotient - Math.round(quotient)) < Number.EPSILON * Math.max(1, Math.abs(quotient)) * 4;
|
|
714
762
|
}
|
|
763
|
+
var ACTOR_ENVELOPE_VERSION = 1;
|
|
764
|
+
var TOPOLO_ACTOR_ENVELOPE_HEADER = "X-Topolo-Actor-Envelope";
|
|
765
|
+
var PRINCIPAL_TYPES = /* @__PURE__ */ new Set([
|
|
766
|
+
"human",
|
|
767
|
+
"managed_agent",
|
|
768
|
+
"external_agent",
|
|
769
|
+
"service",
|
|
770
|
+
"browser_automation"
|
|
771
|
+
]);
|
|
772
|
+
var ACCOUNTABLE_SUBJECT_TYPES = /* @__PURE__ */ new Set(["human", "organization"]);
|
|
773
|
+
var CHANNELS = /* @__PURE__ */ new Set(["web", "mobile", "cli", "mcp", "api", "workflow", "browser_automation"]);
|
|
774
|
+
var ASSURANCE_LEVELS = /* @__PURE__ */ new Set(["authenticated", "workload_identity", "user_verified"]);
|
|
775
|
+
var HASH_PATTERN = /^sha256:[a-f0-9]{64}$/;
|
|
776
|
+
var ActorEnvelopeValidationError = class extends Error {
|
|
777
|
+
path;
|
|
778
|
+
constructor(path, message) {
|
|
779
|
+
super(`${path}: ${message}`);
|
|
780
|
+
this.name = "ActorEnvelopeValidationError";
|
|
781
|
+
this.path = path;
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
function parseActorEnvelope(value) {
|
|
785
|
+
const envelope = record(value, "actorEnvelope");
|
|
786
|
+
exactKeys(envelope, [
|
|
787
|
+
"schemaVersion",
|
|
788
|
+
"organizationId",
|
|
789
|
+
"accountableSubject",
|
|
790
|
+
"performer",
|
|
791
|
+
"delegation",
|
|
792
|
+
"channel",
|
|
793
|
+
"assurance",
|
|
794
|
+
"action",
|
|
795
|
+
"issuedAt",
|
|
796
|
+
"expiresAt"
|
|
797
|
+
], "actorEnvelope");
|
|
798
|
+
if (envelope.schemaVersion !== ACTOR_ENVELOPE_VERSION) {
|
|
799
|
+
fail("actorEnvelope.schemaVersion", `must equal ${ACTOR_ENVELOPE_VERSION}`);
|
|
800
|
+
}
|
|
801
|
+
const accountableSubject = parseAccountableSubject(envelope.accountableSubject);
|
|
802
|
+
const performer = parseIdentity(envelope.performer, "actorEnvelope.performer");
|
|
803
|
+
const organizationId = text(envelope.organizationId, "actorEnvelope.organizationId");
|
|
804
|
+
const channel = member(envelope.channel, CHANNELS, "actorEnvelope.channel");
|
|
805
|
+
const issuedAt = timestamp(envelope.issuedAt, "actorEnvelope.issuedAt");
|
|
806
|
+
const expiresAt = envelope.expiresAt === void 0 ? void 0 : timestamp(envelope.expiresAt, "actorEnvelope.expiresAt");
|
|
807
|
+
if (expiresAt && Date.parse(expiresAt) <= Date.parse(issuedAt)) {
|
|
808
|
+
fail("actorEnvelope.expiresAt", "must be later than issuedAt");
|
|
809
|
+
}
|
|
810
|
+
const assuranceValue = record(envelope.assurance, "actorEnvelope.assurance");
|
|
811
|
+
exactKeys(assuranceValue, ["level", "methods"], "actorEnvelope.assurance");
|
|
812
|
+
const assurance = {
|
|
813
|
+
level: member(assuranceValue.level, ASSURANCE_LEVELS, "actorEnvelope.assurance.level"),
|
|
814
|
+
methods: textArray(assuranceValue.methods, "actorEnvelope.assurance.methods")
|
|
815
|
+
};
|
|
816
|
+
const actionValue = record(envelope.action, "actorEnvelope.action");
|
|
817
|
+
exactKeys(actionValue, ["requestId", "actionId", "resourceType", "resourceId", "inputHash"], "actorEnvelope.action");
|
|
818
|
+
const inputHash = text(actionValue.inputHash, "actorEnvelope.action.inputHash");
|
|
819
|
+
if (!HASH_PATTERN.test(inputHash)) fail("actorEnvelope.action.inputHash", "must be a lowercase sha256 digest");
|
|
820
|
+
const action = {
|
|
821
|
+
requestId: text(actionValue.requestId, "actorEnvelope.action.requestId"),
|
|
822
|
+
actionId: text(actionValue.actionId, "actorEnvelope.action.actionId"),
|
|
823
|
+
...actionValue.resourceType === void 0 ? {} : { resourceType: text(actionValue.resourceType, "actorEnvelope.action.resourceType") },
|
|
824
|
+
...actionValue.resourceId === void 0 ? {} : { resourceId: text(actionValue.resourceId, "actorEnvelope.action.resourceId") },
|
|
825
|
+
inputHash
|
|
826
|
+
};
|
|
827
|
+
if (action.resourceType === void 0 !== (action.resourceId === void 0)) {
|
|
828
|
+
fail("actorEnvelope.action", "resourceType and resourceId must be provided together");
|
|
829
|
+
}
|
|
830
|
+
const delegation = parseDelegation(envelope.delegation);
|
|
831
|
+
const directHuman = performer.principalType === "human" && performer.principalId === accountableSubject.principalId;
|
|
832
|
+
if (directHuman && delegation !== null) fail("actorEnvelope.delegation", "must be null for direct human action");
|
|
833
|
+
if (!directHuman && delegation === null) fail("actorEnvelope.delegation", "is required when performer differs from accountable subject");
|
|
834
|
+
if (delegation) {
|
|
835
|
+
const finalHop = delegation.lineage.at(-1);
|
|
836
|
+
if (!finalHop || finalHop.principalId !== performer.principalId || finalHop.principalType !== performer.principalType) {
|
|
837
|
+
fail("actorEnvelope.delegation.lineage", "must end with the performer");
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
if (performer.principalType === "external_agent" && (!performer.installationId || !performer.provider)) {
|
|
841
|
+
fail("actorEnvelope.performer", "external_agent requires installationId and provider");
|
|
842
|
+
}
|
|
843
|
+
if (performer.principalType === "managed_agent" && !performer.agentInstanceId) {
|
|
844
|
+
fail("actorEnvelope.performer.agentInstanceId", "is required for managed_agent");
|
|
845
|
+
}
|
|
846
|
+
if (performer.principalType === "browser_automation" && (!performer.installationId || !performer.browserSessionId)) {
|
|
847
|
+
fail("actorEnvelope.performer", "browser_automation requires installationId and browserSessionId");
|
|
848
|
+
}
|
|
849
|
+
return {
|
|
850
|
+
schemaVersion: ACTOR_ENVELOPE_VERSION,
|
|
851
|
+
organizationId,
|
|
852
|
+
accountableSubject,
|
|
853
|
+
performer,
|
|
854
|
+
delegation,
|
|
855
|
+
channel,
|
|
856
|
+
assurance,
|
|
857
|
+
action,
|
|
858
|
+
issuedAt,
|
|
859
|
+
...expiresAt ? { expiresAt } : {}
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
function canonicalizeActorEnvelope(value) {
|
|
863
|
+
return canonicalJson(parseActorEnvelope(value));
|
|
864
|
+
}
|
|
865
|
+
function encodeActorEnvelopeHeader(value) {
|
|
866
|
+
const bytes = new TextEncoder().encode(canonicalizeActorEnvelope(value));
|
|
867
|
+
const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
|
|
868
|
+
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
869
|
+
}
|
|
870
|
+
function canonicalJson(value) {
|
|
871
|
+
if (value === null) return "null";
|
|
872
|
+
if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
|
|
873
|
+
if (typeof value === "number") {
|
|
874
|
+
if (!Number.isFinite(value)) throw new TypeError("Canonical JSON accepts finite numbers only.");
|
|
875
|
+
return JSON.stringify(value);
|
|
876
|
+
}
|
|
877
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
878
|
+
if (typeof value !== "object" || !value || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
|
|
879
|
+
throw new TypeError("Canonical JSON accepts JSON objects, arrays, and primitives only.");
|
|
880
|
+
}
|
|
881
|
+
const entries = Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right));
|
|
882
|
+
return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
|
|
883
|
+
}
|
|
884
|
+
function parseAccountableSubject(value) {
|
|
885
|
+
const path = "actorEnvelope.accountableSubject";
|
|
886
|
+
const subject = record(value, path);
|
|
887
|
+
exactKeys(subject, ["principalType", "principalId", "displayName"], path);
|
|
888
|
+
return {
|
|
889
|
+
principalType: member(subject.principalType, ACCOUNTABLE_SUBJECT_TYPES, `${path}.principalType`),
|
|
890
|
+
principalId: text(subject.principalId, `${path}.principalId`),
|
|
891
|
+
...subject.displayName === void 0 ? {} : { displayName: text(subject.displayName, `${path}.displayName`) }
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
async function sha256CanonicalValue(value) {
|
|
895
|
+
const bytes = new TextEncoder().encode(canonicalJson(value));
|
|
896
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
897
|
+
return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
898
|
+
}
|
|
899
|
+
function parseIdentity(value, path) {
|
|
900
|
+
const identity = record(value, path);
|
|
901
|
+
exactKeys(identity, [
|
|
902
|
+
"principalType",
|
|
903
|
+
"principalId",
|
|
904
|
+
"displayName",
|
|
905
|
+
"installationId",
|
|
906
|
+
"agentInstanceId",
|
|
907
|
+
"browserSessionId",
|
|
908
|
+
"provider",
|
|
909
|
+
"model"
|
|
910
|
+
], path);
|
|
911
|
+
return {
|
|
912
|
+
principalType: member(identity.principalType, PRINCIPAL_TYPES, `${path}.principalType`),
|
|
913
|
+
principalId: text(identity.principalId, `${path}.principalId`),
|
|
914
|
+
...optionalTextFields(identity, path, [
|
|
915
|
+
"displayName",
|
|
916
|
+
"installationId",
|
|
917
|
+
"agentInstanceId",
|
|
918
|
+
"browserSessionId",
|
|
919
|
+
"provider",
|
|
920
|
+
"model"
|
|
921
|
+
])
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
function parseDelegation(value) {
|
|
925
|
+
if (value === null) return null;
|
|
926
|
+
const delegation = record(value, "actorEnvelope.delegation");
|
|
927
|
+
exactKeys(delegation, ["taskId", "runId", "lineage"], "actorEnvelope.delegation");
|
|
928
|
+
if (!Array.isArray(delegation.lineage) || delegation.lineage.length === 0 || delegation.lineage.length > 16) {
|
|
929
|
+
fail("actorEnvelope.delegation.lineage", "must contain between 1 and 16 hops");
|
|
930
|
+
}
|
|
931
|
+
return {
|
|
932
|
+
taskId: text(delegation.taskId, "actorEnvelope.delegation.taskId"),
|
|
933
|
+
runId: text(delegation.runId, "actorEnvelope.delegation.runId"),
|
|
934
|
+
lineage: delegation.lineage.map((value2, index) => {
|
|
935
|
+
const hop = record(value2, `actorEnvelope.delegation.lineage[${index}]`);
|
|
936
|
+
exactKeys(hop, ["principalType", "principalId"], `actorEnvelope.delegation.lineage[${index}]`);
|
|
937
|
+
return {
|
|
938
|
+
principalType: member(hop.principalType, PRINCIPAL_TYPES, `actorEnvelope.delegation.lineage[${index}].principalType`),
|
|
939
|
+
principalId: text(hop.principalId, `actorEnvelope.delegation.lineage[${index}].principalId`)
|
|
940
|
+
};
|
|
941
|
+
})
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
function record(value, path) {
|
|
945
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "must be an object");
|
|
946
|
+
return value;
|
|
947
|
+
}
|
|
948
|
+
function exactKeys(value, allowed, path) {
|
|
949
|
+
const unknown = Object.keys(value).filter((key) => !allowed.includes(key));
|
|
950
|
+
if (unknown.length) fail(path, `contains unknown field ${unknown[0]}`);
|
|
951
|
+
}
|
|
952
|
+
function text(value, path) {
|
|
953
|
+
if (typeof value !== "string" || value.trim() !== value || value.length === 0 || value.length > 256) {
|
|
954
|
+
fail(path, "must be a non-empty trimmed string of at most 256 characters");
|
|
955
|
+
}
|
|
956
|
+
return value;
|
|
957
|
+
}
|
|
958
|
+
function timestamp(value, path) {
|
|
959
|
+
const result = text(value, path);
|
|
960
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(result) || Number.isNaN(Date.parse(result))) {
|
|
961
|
+
fail(path, "must be an RFC 3339 UTC timestamp");
|
|
962
|
+
}
|
|
963
|
+
return result;
|
|
964
|
+
}
|
|
965
|
+
function member(value, values, path) {
|
|
966
|
+
if (typeof value !== "string" || !values.has(value)) fail(path, "contains an unsupported value");
|
|
967
|
+
return value;
|
|
968
|
+
}
|
|
969
|
+
function textArray(value, path) {
|
|
970
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > 16) fail(path, "must contain between 1 and 16 values");
|
|
971
|
+
return value.map((item, index) => text(item, `${path}[${index}]`));
|
|
972
|
+
}
|
|
973
|
+
function optionalTextFields(value, path, keys) {
|
|
974
|
+
return Object.fromEntries(keys.flatMap((key) => value[key] === void 0 ? [] : [[key, text(value[key], `${path}.${key}`)]]));
|
|
975
|
+
}
|
|
976
|
+
function fail(path, message) {
|
|
977
|
+
throw new ActorEnvelopeValidationError(path, message);
|
|
978
|
+
}
|
|
715
979
|
function parseTopoloActionUploadContract(value) {
|
|
716
980
|
if (!isRecord2(value) || value.profile !== "inline-base64" && value.profile !== "url-session") {
|
|
717
981
|
throw new TopoloSdkError("upload_contract_unavailable", "This action does not publish a supported upload contract.");
|
|
@@ -916,6 +1180,7 @@ var NEGATIVE_CACHE_TTL_MS = 60 * 1e3;
|
|
|
916
1180
|
var TopoloClient = class _TopoloClient {
|
|
917
1181
|
credential;
|
|
918
1182
|
agent;
|
|
1183
|
+
actorContext;
|
|
919
1184
|
serviceUrls;
|
|
920
1185
|
environment;
|
|
921
1186
|
appIds;
|
|
@@ -923,7 +1188,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
923
1188
|
timeoutMs;
|
|
924
1189
|
fetchImpl;
|
|
925
1190
|
credentialProvider;
|
|
926
|
-
|
|
1191
|
+
credentialLoadsInFlight = /* @__PURE__ */ new Map();
|
|
927
1192
|
refreshCredential;
|
|
928
1193
|
refreshInFlight = null;
|
|
929
1194
|
debug;
|
|
@@ -946,6 +1211,11 @@ var TopoloClient = class _TopoloClient {
|
|
|
946
1211
|
if (!options.agent?.clientName) throw new TopoloAuthError("agent.clientName is required");
|
|
947
1212
|
this.credential = options.credential;
|
|
948
1213
|
this.agent = options.agent;
|
|
1214
|
+
this.actorContext = options.actorContext ? {
|
|
1215
|
+
channel: options.actorContext.channel,
|
|
1216
|
+
taskId: options.actorContext.taskId ?? `task_${generateRequestId()}`,
|
|
1217
|
+
runId: options.actorContext.runId ?? `run_${generateRequestId()}`
|
|
1218
|
+
} : void 0;
|
|
949
1219
|
this.environment = options.environment;
|
|
950
1220
|
this.serviceUrls = options.environment ? topoloServiceOrigins(options.serviceUrls, options.environment) : options.serviceUrls;
|
|
951
1221
|
this.appIds = options.appIds;
|
|
@@ -975,7 +1245,16 @@ var TopoloClient = class _TopoloClient {
|
|
|
975
1245
|
`Mutating ${method} requests require { confirm: true }. This guardrail prevents agents from issuing writes without an explicit human-in-the-loop acknowledgement.`
|
|
976
1246
|
);
|
|
977
1247
|
}
|
|
978
|
-
|
|
1248
|
+
const actionId = requestActionId(opts.headers);
|
|
1249
|
+
const taskId = opts.actorEnvelope?.delegation?.taskId ?? this.actorContext?.taskId;
|
|
1250
|
+
let requestCredential = await this.loadCurrentCredential({
|
|
1251
|
+
service: normalizeLookupKey(opts.service),
|
|
1252
|
+
method,
|
|
1253
|
+
path: opts.path,
|
|
1254
|
+
...actionId ? { actionId } : {},
|
|
1255
|
+
...taskId ? { taskId } : {},
|
|
1256
|
+
...opts.resource ? { resource: opts.resource } : {}
|
|
1257
|
+
});
|
|
979
1258
|
const resolvedService = opts.baseUrl ? { serviceKey: normalizeLookupKey(opts.service), baseUrl: this.validatedServiceOrigin(opts.service, opts.baseUrl) } : await this.resolveRequestService(opts.service);
|
|
980
1259
|
const baseUrl = resolvedService.baseUrl;
|
|
981
1260
|
const url = new URL(opts.path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
@@ -998,12 +1277,15 @@ var TopoloClient = class _TopoloClient {
|
|
|
998
1277
|
if (opts.body !== void 0) headers.set("Content-Type", "application/json");
|
|
999
1278
|
const platformAppId = resolveConfiguredAppId(resolvedService.serviceKey, this.appIds);
|
|
1000
1279
|
if (platformAppId) headers.set("X-App-ID", platformAppId);
|
|
1001
|
-
|
|
1002
|
-
const requestId = generateRequestId();
|
|
1280
|
+
const actorEnvelope = opts.actorEnvelope === void 0 ? void 0 : parseActorEnvelope(opts.actorEnvelope);
|
|
1281
|
+
const requestId = actorEnvelope?.action.requestId ?? generateRequestId();
|
|
1003
1282
|
applyAuditHeaders(headers, this.agent, requestId);
|
|
1004
1283
|
if (opts.headers) {
|
|
1005
1284
|
for (const [k, v] of Object.entries(opts.headers)) headers.set(k, v);
|
|
1006
1285
|
}
|
|
1286
|
+
if (actorEnvelope) {
|
|
1287
|
+
headers.set(TOPOLO_ACTOR_ENVELOPE_HEADER, encodeActorEnvelopeHeader(actorEnvelope));
|
|
1288
|
+
}
|
|
1007
1289
|
if (opts.resource) {
|
|
1008
1290
|
const resourceType = opts.resource.resourceType.trim();
|
|
1009
1291
|
const resourceId = opts.resource.resourceId.trim();
|
|
@@ -1028,9 +1310,19 @@ var TopoloClient = class _TopoloClient {
|
|
|
1028
1310
|
}, this.timeoutMs);
|
|
1029
1311
|
const signal = opts.signal ? mergeSignals(opts.signal, controller.signal) : controller.signal;
|
|
1030
1312
|
try {
|
|
1313
|
+
const requestHeaders = new Headers(headers);
|
|
1314
|
+
applyAuthHeaders(requestHeaders, requestCredential);
|
|
1315
|
+
if (requestCredential.kind === "dpop_access_token") {
|
|
1316
|
+
requestHeaders.set("DPoP", await createDpopProof({
|
|
1317
|
+
accessToken: requestCredential.accessToken,
|
|
1318
|
+
method,
|
|
1319
|
+
targetUrl: urlStr,
|
|
1320
|
+
privateJwk: requestCredential.privateJwk
|
|
1321
|
+
}));
|
|
1322
|
+
}
|
|
1031
1323
|
return await this.fetchImpl(urlStr, {
|
|
1032
1324
|
method,
|
|
1033
|
-
headers:
|
|
1325
|
+
headers: requestHeaders,
|
|
1034
1326
|
body: opts.rawBody ?? (opts.body !== void 0 ? JSON.stringify(opts.body) : null),
|
|
1035
1327
|
signal
|
|
1036
1328
|
});
|
|
@@ -1061,13 +1353,13 @@ var TopoloClient = class _TopoloClient {
|
|
|
1061
1353
|
};
|
|
1062
1354
|
let res;
|
|
1063
1355
|
try {
|
|
1064
|
-
const attemptedCredential =
|
|
1356
|
+
const attemptedCredential = requestCredential;
|
|
1065
1357
|
res = await fetchWithReadRetry();
|
|
1066
1358
|
if (res.status === 401 && attemptedCredential.kind === "access_token" && this.refreshCredential) {
|
|
1067
1359
|
await res.body?.cancel().catch(() => void 0);
|
|
1068
1360
|
const refreshed = await this.refreshRejectedAccessToken(attemptedCredential.accessToken);
|
|
1069
1361
|
if (refreshed) {
|
|
1070
|
-
|
|
1362
|
+
requestCredential = refreshed;
|
|
1071
1363
|
res = await fetchWithReadRetry();
|
|
1072
1364
|
}
|
|
1073
1365
|
}
|
|
@@ -1157,18 +1449,23 @@ var TopoloClient = class _TopoloClient {
|
|
|
1157
1449
|
});
|
|
1158
1450
|
return parsed;
|
|
1159
1451
|
}
|
|
1160
|
-
async loadCurrentCredential() {
|
|
1161
|
-
if (!this.credentialProvider) return;
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1452
|
+
async loadCurrentCredential(context) {
|
|
1453
|
+
if (!this.credentialProvider) return this.credential;
|
|
1454
|
+
const key = JSON.stringify(context);
|
|
1455
|
+
let load = this.credentialLoadsInFlight.get(key);
|
|
1456
|
+
if (!load) {
|
|
1457
|
+
load = this.credentialProvider(context).then((credential) => {
|
|
1458
|
+
if (!credential) {
|
|
1459
|
+
throw new TopoloAuthError("The configured Topolo credential is no longer available.");
|
|
1460
|
+
}
|
|
1461
|
+
this.credential = credential;
|
|
1462
|
+
return credential;
|
|
1463
|
+
}).finally(() => {
|
|
1464
|
+
this.credentialLoadsInFlight.delete(key);
|
|
1465
|
+
});
|
|
1466
|
+
this.credentialLoadsInFlight.set(key, load);
|
|
1467
|
+
}
|
|
1468
|
+
return load;
|
|
1172
1469
|
}
|
|
1173
1470
|
async refreshRejectedAccessToken(attemptedAccessToken) {
|
|
1174
1471
|
if (this.credential.kind === "access_token" && this.credential.accessToken !== attemptedAccessToken) {
|
|
@@ -1450,14 +1747,14 @@ var TopoloClient = class _TopoloClient {
|
|
|
1450
1747
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
1451
1748
|
throw new TopoloSdkError("action_catalog_summary_unavailable", "Action catalog summary is unavailable.");
|
|
1452
1749
|
}
|
|
1453
|
-
const
|
|
1454
|
-
if (
|
|
1750
|
+
const record2 = raw;
|
|
1751
|
+
if (record2.algorithm !== "sha256-v1" || typeof record2.digest !== "string" || !/^[a-f0-9]{64}$/.test(record2.digest) || !Number.isSafeInteger(record2.actionCount) || Number(record2.actionCount) < 0) {
|
|
1455
1752
|
throw new TopoloSdkError("action_catalog_summary_unavailable", "Action catalog summary is invalid.");
|
|
1456
1753
|
}
|
|
1457
1754
|
return {
|
|
1458
1755
|
algorithm: "sha256-v1",
|
|
1459
|
-
digest:
|
|
1460
|
-
actionCount: Number(
|
|
1756
|
+
digest: record2.digest,
|
|
1757
|
+
actionCount: Number(record2.actionCount)
|
|
1461
1758
|
};
|
|
1462
1759
|
}
|
|
1463
1760
|
/** @deprecated Prefer searchActions() so callers retain the continuation cursor. */
|
|
@@ -1498,6 +1795,14 @@ var TopoloClient = class _TopoloClient {
|
|
|
1498
1795
|
const entry = await this.getAction(action);
|
|
1499
1796
|
return this.executeResolvedAction(entry, input, options);
|
|
1500
1797
|
}
|
|
1798
|
+
async issueExternalAgentCredential(input) {
|
|
1799
|
+
const actionId = "app_topolo_auth.principal_credentials.issue";
|
|
1800
|
+
const entry = await this.getAction(actionId);
|
|
1801
|
+
if (entry.actionId !== actionId) {
|
|
1802
|
+
throw new TopoloSdkError("unexpected_action", "External agent credential issuance resolved an unexpected action.");
|
|
1803
|
+
}
|
|
1804
|
+
return this.executeResolvedAction(entry, input, { confirm: true }, true);
|
|
1805
|
+
}
|
|
1501
1806
|
async uploadAction(action, content, input, options) {
|
|
1502
1807
|
const entry = await this.getAction(action);
|
|
1503
1808
|
const contract = parseTopoloActionUploadContract(entry.agentContract?.upload);
|
|
@@ -1629,7 +1934,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1629
1934
|
}
|
|
1630
1935
|
return this.executeResolvedAction(entry, input, options);
|
|
1631
1936
|
}
|
|
1632
|
-
async executeResolvedAction(entry, input, options) {
|
|
1937
|
+
async executeResolvedAction(entry, input, options, preserveSensitiveResult = false) {
|
|
1633
1938
|
const agentAccess = entry.agentAccess ?? (entry.destructive ? "off" : entry.readOnly && !entry.requiresConfirmation ? "auto" : "confirm");
|
|
1634
1939
|
if (agentAccess === "off") {
|
|
1635
1940
|
throw new TopoloPermissionError(
|
|
@@ -1657,6 +1962,20 @@ var TopoloClient = class _TopoloClient {
|
|
|
1657
1962
|
);
|
|
1658
1963
|
}
|
|
1659
1964
|
const mapped = mapActionInput(entry, input);
|
|
1965
|
+
const resolvedActorEnvelope = options.actorEnvelope ?? await this.buildVerifiedAgentActorEnvelope(entry.actionId, input, options.resource);
|
|
1966
|
+
if (resolvedActorEnvelope) {
|
|
1967
|
+
const actorEnvelope = parseActorEnvelope(resolvedActorEnvelope);
|
|
1968
|
+
if (actorEnvelope.action.actionId !== entry.actionId) {
|
|
1969
|
+
throw new TopoloSdkError("actor_envelope_action_mismatch", "Actor envelope actionId does not match the executed action.");
|
|
1970
|
+
}
|
|
1971
|
+
if (actorEnvelope.action.inputHash !== await sha256CanonicalValue(input)) {
|
|
1972
|
+
throw new TopoloSdkError("actor_envelope_input_mismatch", "Actor envelope inputHash does not match the executed action input.");
|
|
1973
|
+
}
|
|
1974
|
+
const envelopeResource = actorEnvelope.action.resourceType && actorEnvelope.action.resourceId ? { resourceType: actorEnvelope.action.resourceType, resourceId: actorEnvelope.action.resourceId } : void 0;
|
|
1975
|
+
if (envelopeResource?.resourceType !== options.resource?.resourceType || envelopeResource?.resourceId !== options.resource?.resourceId) {
|
|
1976
|
+
throw new TopoloSdkError("actor_envelope_resource_mismatch", "Actor envelope resource does not match the executed action resource.");
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1660
1979
|
const service = entry.appSlug || entry.appId;
|
|
1661
1980
|
const result = await this.request({
|
|
1662
1981
|
service,
|
|
@@ -1666,13 +1985,64 @@ var TopoloClient = class _TopoloClient {
|
|
|
1666
1985
|
...mapped.query ? { query: mapped.query } : {},
|
|
1667
1986
|
...mapped.body !== void 0 ? { body: mapped.body } : {},
|
|
1668
1987
|
...options.resource ? { resource: options.resource } : {},
|
|
1988
|
+
...resolvedActorEnvelope ? { actorEnvelope: resolvedActorEnvelope } : {},
|
|
1669
1989
|
headers: {
|
|
1670
1990
|
[TOPOLO_ACTION_ID_HEADER]: entry.actionId,
|
|
1671
1991
|
[TOPOLO_ACTION_NAME_HEADER]: entry.name
|
|
1672
1992
|
},
|
|
1673
1993
|
confirm: options.confirm === true || entry.readOnly && !entry.destructive && !entry.requiresConfirmation
|
|
1674
1994
|
});
|
|
1675
|
-
return redactActionResult(result);
|
|
1995
|
+
return preserveSensitiveResult ? result : redactActionResult(result);
|
|
1996
|
+
}
|
|
1997
|
+
async buildVerifiedAgentActorEnvelope(actionId, input, resource) {
|
|
1998
|
+
if (!this.actorContext) return void 0;
|
|
1999
|
+
const identity = await this.introspect();
|
|
2000
|
+
const principalType = identity.user.principalType;
|
|
2001
|
+
if (principalType !== "agent_employee" && principalType !== "external_agent") return void 0;
|
|
2002
|
+
if (!identity.organization || !identity.user.accountableUserId || !identity.user.principalId) {
|
|
2003
|
+
throw new TopoloSdkError(
|
|
2004
|
+
"incomplete_agent_identity",
|
|
2005
|
+
"Verified agent credentials must resolve an organization, accountable user, and principal id."
|
|
2006
|
+
);
|
|
2007
|
+
}
|
|
2008
|
+
const performer = principalType === "external_agent" ? {
|
|
2009
|
+
principalType: "external_agent",
|
|
2010
|
+
principalId: identity.user.principalId,
|
|
2011
|
+
installationId: identity.user.installationId ?? void 0,
|
|
2012
|
+
provider: identity.user.provider ?? void 0,
|
|
2013
|
+
model: identity.user.model ?? void 0
|
|
2014
|
+
} : {
|
|
2015
|
+
principalType: "managed_agent",
|
|
2016
|
+
principalId: identity.user.principalId,
|
|
2017
|
+
agentInstanceId: identity.user.agentInstanceId ?? void 0,
|
|
2018
|
+
model: identity.user.model ?? void 0
|
|
2019
|
+
};
|
|
2020
|
+
if (performer.principalType === "external_agent" && (!performer.installationId || !performer.provider) || performer.principalType === "managed_agent" && !performer.agentInstanceId) {
|
|
2021
|
+
throw new TopoloSdkError(
|
|
2022
|
+
"incomplete_agent_identity",
|
|
2023
|
+
"Verified agent credentials are missing the performer identity required for provenance."
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
2026
|
+
return parseActorEnvelope({
|
|
2027
|
+
schemaVersion: 1,
|
|
2028
|
+
organizationId: identity.organization.id,
|
|
2029
|
+
accountableSubject: { principalType: "human", principalId: identity.user.accountableUserId },
|
|
2030
|
+
performer,
|
|
2031
|
+
delegation: {
|
|
2032
|
+
taskId: this.actorContext.taskId,
|
|
2033
|
+
runId: this.actorContext.runId,
|
|
2034
|
+
lineage: [{ principalType: performer.principalType, principalId: performer.principalId }]
|
|
2035
|
+
},
|
|
2036
|
+
channel: this.actorContext.channel,
|
|
2037
|
+
assurance: { level: "workload_identity", methods: ["client_credentials"] },
|
|
2038
|
+
action: {
|
|
2039
|
+
requestId: generateRequestId(),
|
|
2040
|
+
actionId,
|
|
2041
|
+
...resource ? resource : {},
|
|
2042
|
+
inputHash: await sha256CanonicalValue(input)
|
|
2043
|
+
},
|
|
2044
|
+
issuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2045
|
+
});
|
|
1676
2046
|
}
|
|
1677
2047
|
/**
|
|
1678
2048
|
* Introspect the current credential. Returns the caller's resolved identity,
|
|
@@ -1701,7 +2071,18 @@ var TopoloClient = class _TopoloClient {
|
|
|
1701
2071
|
email: user.email,
|
|
1702
2072
|
name: user.name ?? null,
|
|
1703
2073
|
role: user.role ?? (kind === "api_key" ? "service" : "member"),
|
|
1704
|
-
permissions: payload.permissions ?? user.permissions ?? []
|
|
2074
|
+
permissions: payload.permissions ?? user.permissions ?? [],
|
|
2075
|
+
...normalizeAgentPrincipalType(user.principalType) ? {
|
|
2076
|
+
principalId: readOptionalString(user.principalId),
|
|
2077
|
+
principalType: normalizeAgentPrincipalType(user.principalType),
|
|
2078
|
+
accountableUserId: readOptionalString(user.accountableUserId),
|
|
2079
|
+
agentInstanceId: readOptionalString(user.agentInstanceId),
|
|
2080
|
+
installationId: readOptionalString(user.installationId),
|
|
2081
|
+
provider: readOptionalString(user.provider),
|
|
2082
|
+
model: readOptionalString(user.model),
|
|
2083
|
+
serviceScopes: stringArray(user.serviceScopes),
|
|
2084
|
+
policyRef: readOptionalString(user.policyRef)
|
|
2085
|
+
} : {}
|
|
1705
2086
|
},
|
|
1706
2087
|
organization: organization ? {
|
|
1707
2088
|
id: organization.id,
|
|
@@ -1830,12 +2211,21 @@ function redactActionResult(value) {
|
|
|
1830
2211
|
if (!isRecord3(value)) return value;
|
|
1831
2212
|
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
1832
2213
|
key,
|
|
1833
|
-
isSensitiveActionResultKey(key) ? "[REDACTED]" : redactActionResult(item)
|
|
2214
|
+
isSensitiveActionResultKey(key, item) ? "[REDACTED]" : redactActionResult(item)
|
|
1834
2215
|
]));
|
|
1835
2216
|
}
|
|
1836
|
-
function isSensitiveActionResultKey(key) {
|
|
2217
|
+
function isSensitiveActionResultKey(key, value) {
|
|
1837
2218
|
const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1838
|
-
|
|
2219
|
+
if (normalized === "capability") return isCredentialShaped(value);
|
|
2220
|
+
return ["authorization", "cookie", "setcookie", "proxyauthorization"].includes(normalized) || /(token|secret|password|privatekey|apikey)$/.test(normalized);
|
|
2221
|
+
}
|
|
2222
|
+
var PLAIN_ENUM_VALUE = /^[a-z][a-z0-9_-]{0,31}$/;
|
|
2223
|
+
function isCredentialShaped(value) {
|
|
2224
|
+
if (typeof value !== "string") return value !== null && value !== void 0 && typeof value !== "boolean" && typeof value !== "number";
|
|
2225
|
+
const trimmed = value.trim();
|
|
2226
|
+
if (!trimmed) return false;
|
|
2227
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) return true;
|
|
2228
|
+
return !PLAIN_ENUM_VALUE.test(trimmed);
|
|
1839
2229
|
}
|
|
1840
2230
|
function resolveConfiguredServiceUrl(slug, serviceUrls) {
|
|
1841
2231
|
for (const serviceKey of serviceKeyCandidates2(slug)) {
|
|
@@ -2057,6 +2447,9 @@ function normalizeCredentialAuthorizationMode(value, kind) {
|
|
|
2057
2447
|
if (value === "context_rbac" || value === "oauth_scopes" || value === "session_rbac") return value;
|
|
2058
2448
|
return "unknown";
|
|
2059
2449
|
}
|
|
2450
|
+
function normalizeAgentPrincipalType(value) {
|
|
2451
|
+
return value === "agent_employee" || value === "external_agent" ? value : null;
|
|
2452
|
+
}
|
|
2060
2453
|
function describeError(body, fallback) {
|
|
2061
2454
|
if (body && typeof body === "object") {
|
|
2062
2455
|
const maybe = body;
|
|
@@ -2075,16 +2468,22 @@ function pickString(value) {
|
|
|
2075
2468
|
}
|
|
2076
2469
|
return void 0;
|
|
2077
2470
|
}
|
|
2471
|
+
function requestActionId(headers) {
|
|
2472
|
+
const value = Object.entries(headers ?? {}).find(
|
|
2473
|
+
([name]) => name.toLowerCase() === TOPOLO_ACTION_ID_HEADER.toLowerCase()
|
|
2474
|
+
)?.[1];
|
|
2475
|
+
return value?.trim() || void 0;
|
|
2476
|
+
}
|
|
2078
2477
|
function extractRequired(body) {
|
|
2079
2478
|
if (body && typeof body === "object") {
|
|
2080
2479
|
const maybe = body;
|
|
2081
2480
|
if (Array.isArray(maybe.required)) return canonicalTopoloRequired(maybe.required);
|
|
2082
2481
|
for (const nested of [maybe.details, maybe.error]) {
|
|
2083
2482
|
if (!nested || typeof nested !== "object" || Array.isArray(nested)) continue;
|
|
2084
|
-
const
|
|
2085
|
-
if (Array.isArray(
|
|
2086
|
-
if (
|
|
2087
|
-
const required =
|
|
2483
|
+
const record2 = nested;
|
|
2484
|
+
if (Array.isArray(record2.required)) return canonicalTopoloRequired(record2.required);
|
|
2485
|
+
if (record2.details && typeof record2.details === "object" && !Array.isArray(record2.details)) {
|
|
2486
|
+
const required = record2.details.required;
|
|
2088
2487
|
if (Array.isArray(required)) return canonicalTopoloRequired(required);
|
|
2089
2488
|
}
|
|
2090
2489
|
}
|
|
@@ -2159,8 +2558,8 @@ function compactResultValue(value, depth, budget) {
|
|
|
2159
2558
|
return value.length > ACTION_RESULT_MAX_ITEMS ? { count: value.length, items, omittedItems: value.length - ACTION_RESULT_MAX_ITEMS } : items;
|
|
2160
2559
|
}
|
|
2161
2560
|
if (typeof value === "object") {
|
|
2162
|
-
const
|
|
2163
|
-
const allKeys = Object.keys(
|
|
2561
|
+
const record2 = value;
|
|
2562
|
+
const allKeys = Object.keys(record2);
|
|
2164
2563
|
const ranked = allKeys.sort((left, right) => resultKeyPriority(left) - resultKeyPriority(right));
|
|
2165
2564
|
const prioritized = ranked.filter((key) => resultKeyPriority(key) < 2);
|
|
2166
2565
|
const ordinary = ranked.filter((key) => resultKeyPriority(key) === 2);
|
|
@@ -2169,7 +2568,7 @@ function compactResultValue(value, depth, budget) {
|
|
|
2169
2568
|
...prioritized,
|
|
2170
2569
|
...ordinary.slice(0, ordinaryLimit)
|
|
2171
2570
|
].slice(0, ACTION_RESULT_MAX_KEYS);
|
|
2172
|
-
const compact = Object.fromEntries(keys.map((key) => [key, compactResultValue(
|
|
2571
|
+
const compact = Object.fromEntries(keys.map((key) => [key, compactResultValue(record2[key], depth + 1, budget)]));
|
|
2173
2572
|
if (allKeys.length > keys.length) compact["_omittedKeys"] = allKeys.length - keys.length;
|
|
2174
2573
|
return compact;
|
|
2175
2574
|
}
|
|
@@ -2390,6 +2789,461 @@ var WorkspacesModule = class {
|
|
|
2390
2789
|
return { organizationId: identity.organization.id, appId: service.appId };
|
|
2391
2790
|
}
|
|
2392
2791
|
};
|
|
2792
|
+
var nonEmptyString2 = { type: "string", minLength: 1 };
|
|
2793
|
+
var stringArray3 = { type: "array", items: nonEmptyString2 };
|
|
2794
|
+
var actionBinding = {
|
|
2795
|
+
type: "object",
|
|
2796
|
+
properties: {
|
|
2797
|
+
id: nonEmptyString2,
|
|
2798
|
+
label: nonEmptyString2,
|
|
2799
|
+
actionId: nonEmptyString2,
|
|
2800
|
+
resourceType: nonEmptyString2,
|
|
2801
|
+
resourceId: nonEmptyString2,
|
|
2802
|
+
input: { type: "object" },
|
|
2803
|
+
requiresConfirmation: { type: "boolean" },
|
|
2804
|
+
destructive: { type: "boolean" },
|
|
2805
|
+
effects: stringArray3,
|
|
2806
|
+
verification: stringArray3,
|
|
2807
|
+
recovery: {
|
|
2808
|
+
type: "object",
|
|
2809
|
+
properties: {
|
|
2810
|
+
mode: { enum: ["not_applicable", "retry", "compensating_action"] },
|
|
2811
|
+
actionId: nonEmptyString2,
|
|
2812
|
+
instructions: nonEmptyString2
|
|
2813
|
+
},
|
|
2814
|
+
required: ["mode", "instructions"],
|
|
2815
|
+
additionalProperties: false
|
|
2816
|
+
}
|
|
2817
|
+
},
|
|
2818
|
+
required: [
|
|
2819
|
+
"id",
|
|
2820
|
+
"label",
|
|
2821
|
+
"actionId",
|
|
2822
|
+
"resourceType",
|
|
2823
|
+
"input",
|
|
2824
|
+
"requiresConfirmation",
|
|
2825
|
+
"destructive",
|
|
2826
|
+
"effects",
|
|
2827
|
+
"verification",
|
|
2828
|
+
"recovery"
|
|
2829
|
+
],
|
|
2830
|
+
additionalProperties: false
|
|
2831
|
+
};
|
|
2832
|
+
var dimension = {
|
|
2833
|
+
type: "object",
|
|
2834
|
+
properties: { key: nonEmptyString2, label: nonEmptyString2, value: nonEmptyString2 },
|
|
2835
|
+
required: ["key", "label", "value"],
|
|
2836
|
+
additionalProperties: false
|
|
2837
|
+
};
|
|
2838
|
+
var period = {
|
|
2839
|
+
type: "object",
|
|
2840
|
+
properties: {
|
|
2841
|
+
start: nonEmptyString2,
|
|
2842
|
+
end: nonEmptyString2,
|
|
2843
|
+
timezone: nonEmptyString2
|
|
2844
|
+
},
|
|
2845
|
+
required: ["start", "end", "timezone"],
|
|
2846
|
+
additionalProperties: false
|
|
2847
|
+
};
|
|
2848
|
+
var series = {
|
|
2849
|
+
type: "object",
|
|
2850
|
+
properties: {
|
|
2851
|
+
id: nonEmptyString2,
|
|
2852
|
+
label: nonEmptyString2,
|
|
2853
|
+
unit: nonEmptyString2,
|
|
2854
|
+
points: {
|
|
2855
|
+
type: "array",
|
|
2856
|
+
items: {
|
|
2857
|
+
type: "object",
|
|
2858
|
+
properties: {
|
|
2859
|
+
timestamp: nonEmptyString2,
|
|
2860
|
+
value: { type: "number" },
|
|
2861
|
+
dimensions: { type: "array", items: dimension }
|
|
2862
|
+
},
|
|
2863
|
+
required: ["timestamp", "value", "dimensions"],
|
|
2864
|
+
additionalProperties: false
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
},
|
|
2868
|
+
required: ["id", "label", "unit", "points"],
|
|
2869
|
+
additionalProperties: false
|
|
2870
|
+
};
|
|
2871
|
+
var contribution = {
|
|
2872
|
+
type: "object",
|
|
2873
|
+
properties: {
|
|
2874
|
+
summary: nonEmptyString2,
|
|
2875
|
+
identity: {
|
|
2876
|
+
type: "object",
|
|
2877
|
+
properties: { iconSource: { const: "catalog" } },
|
|
2878
|
+
required: ["iconSource"],
|
|
2879
|
+
additionalProperties: false
|
|
2880
|
+
},
|
|
2881
|
+
workspaceSelector: {
|
|
2882
|
+
type: "object",
|
|
2883
|
+
properties: {
|
|
2884
|
+
mode: { enum: ["none", "optional", "required"] },
|
|
2885
|
+
resourceType: nonEmptyString2,
|
|
2886
|
+
label: nonEmptyString2,
|
|
2887
|
+
supportsMultiple: { type: "boolean" }
|
|
2888
|
+
},
|
|
2889
|
+
required: ["mode", "resourceType", "label", "supportsMultiple"],
|
|
2890
|
+
additionalProperties: false
|
|
2891
|
+
},
|
|
2892
|
+
metricDefinitions: {
|
|
2893
|
+
type: "array",
|
|
2894
|
+
items: {
|
|
2895
|
+
type: "object",
|
|
2896
|
+
properties: {
|
|
2897
|
+
key: nonEmptyString2,
|
|
2898
|
+
label: nonEmptyString2,
|
|
2899
|
+
description: nonEmptyString2,
|
|
2900
|
+
unit: nonEmptyString2,
|
|
2901
|
+
semantic: {
|
|
2902
|
+
enum: ["gauge", "counter", "rate", "duration", "currency", "percentage"]
|
|
2903
|
+
},
|
|
2904
|
+
aggregation: {
|
|
2905
|
+
enum: ["last", "sum", "average", "minimum", "maximum", "count", "distinct_count"]
|
|
2906
|
+
}
|
|
2907
|
+
},
|
|
2908
|
+
required: ["key", "label", "description", "unit", "semantic", "aggregation"],
|
|
2909
|
+
additionalProperties: false
|
|
2910
|
+
}
|
|
2911
|
+
},
|
|
2912
|
+
seriesDefinitions: {
|
|
2913
|
+
type: "array",
|
|
2914
|
+
items: {
|
|
2915
|
+
type: "object",
|
|
2916
|
+
properties: {
|
|
2917
|
+
key: nonEmptyString2,
|
|
2918
|
+
label: nonEmptyString2,
|
|
2919
|
+
metricKey: nonEmptyString2,
|
|
2920
|
+
unit: nonEmptyString2
|
|
2921
|
+
},
|
|
2922
|
+
required: ["key", "label", "metricKey", "unit"],
|
|
2923
|
+
additionalProperties: false
|
|
2924
|
+
}
|
|
2925
|
+
},
|
|
2926
|
+
dimensions: {
|
|
2927
|
+
type: "array",
|
|
2928
|
+
items: {
|
|
2929
|
+
type: "object",
|
|
2930
|
+
properties: {
|
|
2931
|
+
key: nonEmptyString2,
|
|
2932
|
+
label: nonEmptyString2,
|
|
2933
|
+
description: nonEmptyString2,
|
|
2934
|
+
valueType: { enum: ["string", "number", "boolean", "date", "datetime"] }
|
|
2935
|
+
},
|
|
2936
|
+
required: ["key", "label", "description", "valueType"],
|
|
2937
|
+
additionalProperties: false
|
|
2938
|
+
}
|
|
2939
|
+
},
|
|
2940
|
+
lifecycleStages: {
|
|
2941
|
+
type: "array",
|
|
2942
|
+
items: {
|
|
2943
|
+
type: "object",
|
|
2944
|
+
properties: {
|
|
2945
|
+
key: nonEmptyString2,
|
|
2946
|
+
label: nonEmptyString2,
|
|
2947
|
+
order: { type: "number" },
|
|
2948
|
+
terminal: { type: "boolean" }
|
|
2949
|
+
},
|
|
2950
|
+
required: ["key", "label", "order", "terminal"],
|
|
2951
|
+
additionalProperties: false
|
|
2952
|
+
}
|
|
2953
|
+
},
|
|
2954
|
+
events: {
|
|
2955
|
+
type: "array",
|
|
2956
|
+
items: {
|
|
2957
|
+
type: "object",
|
|
2958
|
+
properties: {
|
|
2959
|
+
key: nonEmptyString2,
|
|
2960
|
+
label: nonEmptyString2,
|
|
2961
|
+
description: nonEmptyString2
|
|
2962
|
+
},
|
|
2963
|
+
required: ["key", "label", "description"],
|
|
2964
|
+
additionalProperties: false
|
|
2965
|
+
}
|
|
2966
|
+
},
|
|
2967
|
+
datasets: {
|
|
2968
|
+
type: "array",
|
|
2969
|
+
minItems: 1,
|
|
2970
|
+
items: {
|
|
2971
|
+
type: "object",
|
|
2972
|
+
properties: {
|
|
2973
|
+
id: nonEmptyString2,
|
|
2974
|
+
label: nonEmptyString2,
|
|
2975
|
+
description: nonEmptyString2,
|
|
2976
|
+
grain: nonEmptyString2,
|
|
2977
|
+
metricKeys: stringArray3,
|
|
2978
|
+
seriesKeys: stringArray3,
|
|
2979
|
+
dimensionKeys: stringArray3,
|
|
2980
|
+
eventKeys: stringArray3,
|
|
2981
|
+
actionIds: stringArray3
|
|
2982
|
+
},
|
|
2983
|
+
required: [
|
|
2984
|
+
"id",
|
|
2985
|
+
"label",
|
|
2986
|
+
"description",
|
|
2987
|
+
"grain",
|
|
2988
|
+
"metricKeys",
|
|
2989
|
+
"seriesKeys",
|
|
2990
|
+
"dimensionKeys",
|
|
2991
|
+
"eventKeys",
|
|
2992
|
+
"actionIds"
|
|
2993
|
+
],
|
|
2994
|
+
additionalProperties: false
|
|
2995
|
+
}
|
|
2996
|
+
},
|
|
2997
|
+
compatibleVisualizations: {
|
|
2998
|
+
type: "array",
|
|
2999
|
+
minItems: 1,
|
|
3000
|
+
items: {
|
|
3001
|
+
enum: ["number", "line", "area", "bar", "donut", "table", "funnel", "timeline", "actions"]
|
|
3002
|
+
}
|
|
3003
|
+
},
|
|
3004
|
+
defaultVisualization: {
|
|
3005
|
+
enum: ["number", "line", "area", "bar", "donut", "table", "funnel", "timeline", "actions"]
|
|
3006
|
+
}
|
|
3007
|
+
},
|
|
3008
|
+
required: [
|
|
3009
|
+
"summary",
|
|
3010
|
+
"identity",
|
|
3011
|
+
"workspaceSelector",
|
|
3012
|
+
"metricDefinitions",
|
|
3013
|
+
"seriesDefinitions",
|
|
3014
|
+
"dimensions",
|
|
3015
|
+
"lifecycleStages",
|
|
3016
|
+
"events",
|
|
3017
|
+
"datasets",
|
|
3018
|
+
"compatibleVisualizations",
|
|
3019
|
+
"defaultVisualization"
|
|
3020
|
+
],
|
|
3021
|
+
additionalProperties: false
|
|
3022
|
+
};
|
|
3023
|
+
var widgetItem = {
|
|
3024
|
+
type: "object",
|
|
3025
|
+
properties: {
|
|
3026
|
+
id: nonEmptyString2,
|
|
3027
|
+
version: nonEmptyString2,
|
|
3028
|
+
appId: nonEmptyString2,
|
|
3029
|
+
serviceName: nonEmptyString2,
|
|
3030
|
+
lastUpdated: nonEmptyString2,
|
|
3031
|
+
type: { enum: ["stats", "chart", "activity", "notifications", "actions"] },
|
|
3032
|
+
presentation: {
|
|
3033
|
+
type: "object",
|
|
3034
|
+
properties: {
|
|
3035
|
+
title: nonEmptyString2,
|
|
3036
|
+
description: nonEmptyString2,
|
|
3037
|
+
contexts: {
|
|
3038
|
+
type: "array",
|
|
3039
|
+
minItems: 1,
|
|
3040
|
+
items: { enum: ["personal", "organization", "workspace", "resource"] }
|
|
3041
|
+
},
|
|
3042
|
+
supportedSizes: {
|
|
3043
|
+
type: "array",
|
|
3044
|
+
minItems: 1,
|
|
3045
|
+
items: { enum: ["compact", "medium", "wide", "full"] }
|
|
3046
|
+
},
|
|
3047
|
+
defaultSize: { enum: ["compact", "medium", "wide", "full"] }
|
|
3048
|
+
},
|
|
3049
|
+
required: ["title", "contexts", "supportedSizes", "defaultSize"],
|
|
3050
|
+
additionalProperties: false
|
|
3051
|
+
},
|
|
3052
|
+
resource: {
|
|
3053
|
+
type: "object",
|
|
3054
|
+
properties: {
|
|
3055
|
+
resourceType: nonEmptyString2,
|
|
3056
|
+
resourceId: nonEmptyString2,
|
|
3057
|
+
requiredPermissions: stringArray3
|
|
3058
|
+
},
|
|
3059
|
+
required: ["resourceType", "requiredPermissions"],
|
|
3060
|
+
additionalProperties: false
|
|
3061
|
+
},
|
|
3062
|
+
state: {
|
|
3063
|
+
type: "object",
|
|
3064
|
+
properties: {
|
|
3065
|
+
status: { enum: ["ready", "empty", "unavailable", "stale", "error"] },
|
|
3066
|
+
observedAt: nonEmptyString2,
|
|
3067
|
+
lastSuccessfulAt: nonEmptyString2,
|
|
3068
|
+
code: nonEmptyString2,
|
|
3069
|
+
message: nonEmptyString2
|
|
3070
|
+
},
|
|
3071
|
+
required: ["status", "observedAt"],
|
|
3072
|
+
additionalProperties: false
|
|
3073
|
+
},
|
|
3074
|
+
refresh: {
|
|
3075
|
+
type: "object",
|
|
3076
|
+
properties: {
|
|
3077
|
+
intervalSeconds: { type: "number" },
|
|
3078
|
+
timeoutSeconds: { type: "number" },
|
|
3079
|
+
maxAgeSeconds: { type: "number" },
|
|
3080
|
+
staleWhileRevalidateSeconds: { type: "number" }
|
|
3081
|
+
},
|
|
3082
|
+
required: ["intervalSeconds", "timeoutSeconds", "maxAgeSeconds", "staleWhileRevalidateSeconds"],
|
|
3083
|
+
additionalProperties: false
|
|
3084
|
+
},
|
|
3085
|
+
configuration: {
|
|
3086
|
+
type: "object",
|
|
3087
|
+
properties: {
|
|
3088
|
+
allowMultipleInstances: { type: "boolean" },
|
|
3089
|
+
fields: {
|
|
3090
|
+
type: "array",
|
|
3091
|
+
items: {
|
|
3092
|
+
type: "object",
|
|
3093
|
+
properties: {
|
|
3094
|
+
key: nonEmptyString2,
|
|
3095
|
+
label: nonEmptyString2,
|
|
3096
|
+
type: { enum: ["string", "number", "boolean", "select", "multi-select", "date-range"] },
|
|
3097
|
+
required: { type: "boolean" },
|
|
3098
|
+
options: {
|
|
3099
|
+
type: "array",
|
|
3100
|
+
items: {
|
|
3101
|
+
type: "object",
|
|
3102
|
+
properties: { value: nonEmptyString2, label: nonEmptyString2 },
|
|
3103
|
+
required: ["value", "label"],
|
|
3104
|
+
additionalProperties: false
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
},
|
|
3108
|
+
required: ["key", "label", "type", "required"],
|
|
3109
|
+
additionalProperties: false
|
|
3110
|
+
}
|
|
3111
|
+
},
|
|
3112
|
+
defaults: { type: "object" }
|
|
3113
|
+
},
|
|
3114
|
+
required: ["allowMultipleInstances", "fields", "defaults"],
|
|
3115
|
+
additionalProperties: false
|
|
3116
|
+
},
|
|
3117
|
+
drilldowns: {
|
|
3118
|
+
type: "array",
|
|
3119
|
+
items: {
|
|
3120
|
+
type: "object",
|
|
3121
|
+
properties: { id: nonEmptyString2, label: nonEmptyString2, href: nonEmptyString2 },
|
|
3122
|
+
required: ["id", "label", "href"],
|
|
3123
|
+
additionalProperties: false
|
|
3124
|
+
}
|
|
3125
|
+
},
|
|
3126
|
+
controls: { type: "array", items: actionBinding },
|
|
3127
|
+
localization: {
|
|
3128
|
+
type: "object",
|
|
3129
|
+
properties: {
|
|
3130
|
+
locale: nonEmptyString2,
|
|
3131
|
+
titleKey: nonEmptyString2,
|
|
3132
|
+
descriptionKey: nonEmptyString2,
|
|
3133
|
+
title: nonEmptyString2,
|
|
3134
|
+
description: nonEmptyString2
|
|
3135
|
+
},
|
|
3136
|
+
required: ["locale", "titleKey"],
|
|
3137
|
+
additionalProperties: false
|
|
3138
|
+
},
|
|
3139
|
+
accessibility: {
|
|
3140
|
+
type: "object",
|
|
3141
|
+
properties: { label: nonEmptyString2, description: nonEmptyString2 },
|
|
3142
|
+
required: ["label"],
|
|
3143
|
+
additionalProperties: false
|
|
3144
|
+
},
|
|
3145
|
+
contribution,
|
|
3146
|
+
metrics: {
|
|
3147
|
+
type: "array",
|
|
3148
|
+
items: {
|
|
3149
|
+
type: "object",
|
|
3150
|
+
properties: {
|
|
3151
|
+
key: nonEmptyString2,
|
|
3152
|
+
label: nonEmptyString2,
|
|
3153
|
+
value: { type: "number" },
|
|
3154
|
+
unit: nonEmptyString2,
|
|
3155
|
+
semantic: {
|
|
3156
|
+
enum: ["gauge", "counter", "rate", "duration", "currency", "percentage"]
|
|
3157
|
+
},
|
|
3158
|
+
aggregation: {
|
|
3159
|
+
enum: ["last", "sum", "average", "minimum", "maximum", "count", "distinct_count"]
|
|
3160
|
+
},
|
|
3161
|
+
period,
|
|
3162
|
+
dimensions: { type: "array", items: dimension },
|
|
3163
|
+
series
|
|
3164
|
+
},
|
|
3165
|
+
required: ["key", "label", "value", "unit", "semantic", "aggregation", "period", "dimensions"],
|
|
3166
|
+
additionalProperties: false
|
|
3167
|
+
}
|
|
3168
|
+
},
|
|
3169
|
+
chartType: { enum: ["line", "bar", "area", "pie", "donut"] },
|
|
3170
|
+
series: { type: "array", items: series },
|
|
3171
|
+
activities: { type: "array", items: { type: "object" } },
|
|
3172
|
+
notifications: { type: "array", items: { type: "object" } },
|
|
3173
|
+
unreadCount: { type: "number" },
|
|
3174
|
+
actions: { type: "array", items: actionBinding }
|
|
3175
|
+
},
|
|
3176
|
+
required: [
|
|
3177
|
+
"id",
|
|
3178
|
+
"version",
|
|
3179
|
+
"appId",
|
|
3180
|
+
"serviceName",
|
|
3181
|
+
"lastUpdated",
|
|
3182
|
+
"type",
|
|
3183
|
+
"presentation",
|
|
3184
|
+
"resource",
|
|
3185
|
+
"state",
|
|
3186
|
+
"refresh",
|
|
3187
|
+
"configuration",
|
|
3188
|
+
"drilldowns",
|
|
3189
|
+
"controls",
|
|
3190
|
+
"localization",
|
|
3191
|
+
"accessibility",
|
|
3192
|
+
"contribution"
|
|
3193
|
+
],
|
|
3194
|
+
allOf: [
|
|
3195
|
+
{ if: { properties: { type: { const: "stats" } } }, then: { required: ["metrics"] } },
|
|
3196
|
+
{ if: { properties: { type: { const: "chart" } } }, then: { required: ["chartType", "series"] } },
|
|
3197
|
+
{ if: { properties: { type: { const: "activity" } } }, then: { required: ["activities"] } },
|
|
3198
|
+
{
|
|
3199
|
+
if: { properties: { type: { const: "notifications" } } },
|
|
3200
|
+
then: { required: ["notifications", "unreadCount"] }
|
|
3201
|
+
},
|
|
3202
|
+
{ if: { properties: { type: { const: "actions" } } }, then: { required: ["actions"] } }
|
|
3203
|
+
],
|
|
3204
|
+
additionalProperties: false
|
|
3205
|
+
};
|
|
3206
|
+
var TOPOLO_WIDGET_OUTPUT_SCHEMA = Object.freeze({
|
|
3207
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
3208
|
+
type: "object",
|
|
3209
|
+
properties: {
|
|
3210
|
+
success: { type: "boolean", const: true },
|
|
3211
|
+
contractVersion: { type: "string", const: "3.0.0" },
|
|
3212
|
+
appId: nonEmptyString2,
|
|
3213
|
+
serviceName: nonEmptyString2,
|
|
3214
|
+
generatedAt: nonEmptyString2,
|
|
3215
|
+
widgets: { type: "array", minItems: 1, items: widgetItem },
|
|
3216
|
+
workspace: {
|
|
3217
|
+
anyOf: [
|
|
3218
|
+
{
|
|
3219
|
+
type: "object",
|
|
3220
|
+
properties: {
|
|
3221
|
+
id: { anyOf: [{ type: "string" }, { type: "number" }] },
|
|
3222
|
+
name: nonEmptyString2
|
|
3223
|
+
},
|
|
3224
|
+
required: ["id", "name"],
|
|
3225
|
+
additionalProperties: true
|
|
3226
|
+
},
|
|
3227
|
+
{ type: "null" }
|
|
3228
|
+
]
|
|
3229
|
+
},
|
|
3230
|
+
workspaces: {
|
|
3231
|
+
type: "array",
|
|
3232
|
+
items: {
|
|
3233
|
+
type: "object",
|
|
3234
|
+
properties: {
|
|
3235
|
+
id: { anyOf: [{ type: "string" }, { type: "number" }] },
|
|
3236
|
+
name: nonEmptyString2,
|
|
3237
|
+
isDefault: { type: "boolean" }
|
|
3238
|
+
},
|
|
3239
|
+
required: ["id", "name"],
|
|
3240
|
+
additionalProperties: false
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
},
|
|
3244
|
+
required: ["success", "contractVersion", "appId", "serviceName", "generatedAt", "widgets"],
|
|
3245
|
+
additionalProperties: false
|
|
3246
|
+
});
|
|
2393
3247
|
function createTopolo(options) {
|
|
2394
3248
|
const client = new TopoloClient(options);
|
|
2395
3249
|
return {
|
|
@@ -2400,10 +3254,15 @@ function createTopolo(options) {
|
|
|
2400
3254
|
};
|
|
2401
3255
|
}
|
|
2402
3256
|
|
|
2403
|
-
// node_modules/@topolo/sdk/dist/node/credential-store.js
|
|
2404
|
-
import { mkdir, readFile, writeFile, chmod, rm, stat } from "fs/promises";
|
|
3257
|
+
// node_modules/.pnpm/@topolo+sdk@0.21.0/node_modules/@topolo/sdk/dist/node/credential-store.js
|
|
3258
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, chmod as chmod2, rm as rm2, stat } from "fs/promises";
|
|
3259
|
+
import { existsSync as existsSync2 } from "fs";
|
|
3260
|
+
import { homedir as homedir2, platform } from "os";
|
|
3261
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
3262
|
+
import { spawnSync } from "child_process";
|
|
3263
|
+
import { mkdir, readFile, readdir, rename, rm, writeFile, chmod } from "fs/promises";
|
|
2405
3264
|
import { existsSync } from "fs";
|
|
2406
|
-
import { homedir, platform } from "os";
|
|
3265
|
+
import { homedir, platform as hostPlatform } from "os";
|
|
2407
3266
|
import { dirname, join } from "path";
|
|
2408
3267
|
var TopoloSdkError2 = class extends Error {
|
|
2409
3268
|
code;
|
|
@@ -2523,10 +3382,10 @@ var TopoloAuthError2 = class extends TopoloSdkError2 {
|
|
|
2523
3382
|
};
|
|
2524
3383
|
function extractTopoloServiceErrorCode2(body) {
|
|
2525
3384
|
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
2526
|
-
const
|
|
2527
|
-
const topLevel = canonicalTopoloErrorCode2(
|
|
3385
|
+
const record2 = body;
|
|
3386
|
+
const topLevel = canonicalTopoloErrorCode2(record2.code);
|
|
2528
3387
|
if (topLevel) return topLevel;
|
|
2529
|
-
const nested =
|
|
3388
|
+
const nested = record2.error;
|
|
2530
3389
|
const nestedString = canonicalTopoloErrorCode2(nested);
|
|
2531
3390
|
if (nestedString) return nestedString;
|
|
2532
3391
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
@@ -2541,14 +3400,14 @@ function serviceErrorCode2(body, fallback) {
|
|
|
2541
3400
|
}
|
|
2542
3401
|
function extractTopoloServiceErrorDetails2(body) {
|
|
2543
3402
|
if (!body || typeof body !== "object" || Array.isArray(body)) return void 0;
|
|
2544
|
-
const
|
|
2545
|
-
let candidate =
|
|
2546
|
-
const nested =
|
|
3403
|
+
const record2 = body;
|
|
3404
|
+
let candidate = record2.details;
|
|
3405
|
+
const nested = record2.error;
|
|
2547
3406
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
2548
3407
|
const nestedRecord = nested;
|
|
2549
3408
|
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
2550
3409
|
}
|
|
2551
|
-
if (candidate === void 0) candidate =
|
|
3410
|
+
if (candidate === void 0) candidate = record2;
|
|
2552
3411
|
if (Array.isArray(candidate)) {
|
|
2553
3412
|
const paths = canonicalTopoloValidationPaths2(candidate);
|
|
2554
3413
|
return paths.length ? { paths } : void 0;
|
|
@@ -2600,8 +3459,8 @@ function canonicalTopoloValidationPaths2(issues) {
|
|
|
2600
3459
|
const paths = [];
|
|
2601
3460
|
for (const issue of issues) {
|
|
2602
3461
|
if (!issue || typeof issue !== "object" || Array.isArray(issue)) continue;
|
|
2603
|
-
const
|
|
2604
|
-
const path = canonicalTopoloValidationPath2(
|
|
3462
|
+
const record2 = issue;
|
|
3463
|
+
const path = canonicalTopoloValidationPath2(record2.field) ?? canonicalTopoloValidationPath2(record2.path);
|
|
2605
3464
|
if (path && !paths.includes(path)) paths.push(path);
|
|
2606
3465
|
if (paths.length === 32) break;
|
|
2607
3466
|
}
|
|
@@ -2811,18 +3670,150 @@ function topoloServiceOrigins2(serviceUrls, environment) {
|
|
|
2811
3670
|
])
|
|
2812
3671
|
);
|
|
2813
3672
|
}
|
|
3673
|
+
var KEYCHAIN_SERVICE = "topolo-external-agent-installation";
|
|
3674
|
+
function installationStorePath() {
|
|
3675
|
+
const override = process.env.TOPOLO_INSTALLATIONS_PATH?.trim();
|
|
3676
|
+
if (override) return override;
|
|
3677
|
+
const base = process.env.XDG_CONFIG_HOME || (hostPlatform() === "win32" ? join(homedir(), "AppData", "Roaming") : join(homedir(), ".config"));
|
|
3678
|
+
return join(base, "topolo", "installations");
|
|
3679
|
+
}
|
|
3680
|
+
function installationPath(environment, id) {
|
|
3681
|
+
if (!/^[A-Za-z0-9_-]+$/.test(id)) throw new TypeError("Installation id contains unsupported characters.");
|
|
3682
|
+
return join(installationStorePath(), environment, `${id}.json`);
|
|
3683
|
+
}
|
|
3684
|
+
function accountName(environment, id) {
|
|
3685
|
+
return `${environment}:${id}`;
|
|
3686
|
+
}
|
|
3687
|
+
function runOrThrow(result, operation) {
|
|
3688
|
+
if (result.status !== 0) {
|
|
3689
|
+
throw new Error(`${operation} failed: ${(result.stderr || result.error?.message || "platform credential store unavailable").trim()}`);
|
|
3690
|
+
}
|
|
3691
|
+
return result.stdout.trim();
|
|
3692
|
+
}
|
|
3693
|
+
function createPlatformInstallationSecretBackend(options = {}) {
|
|
3694
|
+
const platform2 = options.platform ?? hostPlatform();
|
|
3695
|
+
const spawn = options.spawn ?? ((command, args, spawnOptions) => spawnSync(command, args, spawnOptions));
|
|
3696
|
+
const root = options.root ?? installationStorePath();
|
|
3697
|
+
if (platform2 === "darwin") {
|
|
3698
|
+
return {
|
|
3699
|
+
async set(account, value) {
|
|
3700
|
+
runOrThrow(spawn("security", [
|
|
3701
|
+
"add-generic-password",
|
|
3702
|
+
"-U",
|
|
3703
|
+
"-a",
|
|
3704
|
+
account,
|
|
3705
|
+
"-s",
|
|
3706
|
+
KEYCHAIN_SERVICE,
|
|
3707
|
+
"-w"
|
|
3708
|
+
], { input: `${value}
|
|
3709
|
+
`, encoding: "utf8" }), "macOS Keychain write");
|
|
3710
|
+
},
|
|
3711
|
+
async get(account) {
|
|
3712
|
+
const result = spawn("security", [
|
|
3713
|
+
"find-generic-password",
|
|
3714
|
+
"-a",
|
|
3715
|
+
account,
|
|
3716
|
+
"-s",
|
|
3717
|
+
KEYCHAIN_SERVICE,
|
|
3718
|
+
"-w"
|
|
3719
|
+
], { encoding: "utf8" });
|
|
3720
|
+
if (result.status === 44) return null;
|
|
3721
|
+
return runOrThrow(result, "macOS Keychain read");
|
|
3722
|
+
},
|
|
3723
|
+
async delete(account) {
|
|
3724
|
+
const result = spawn("security", [
|
|
3725
|
+
"delete-generic-password",
|
|
3726
|
+
"-a",
|
|
3727
|
+
account,
|
|
3728
|
+
"-s",
|
|
3729
|
+
KEYCHAIN_SERVICE
|
|
3730
|
+
], { encoding: "utf8" });
|
|
3731
|
+
if (result.status !== 44) runOrThrow(result, "macOS Keychain delete");
|
|
3732
|
+
}
|
|
3733
|
+
};
|
|
3734
|
+
}
|
|
3735
|
+
if (platform2 === "linux") {
|
|
3736
|
+
return {
|
|
3737
|
+
async set(account, value) {
|
|
3738
|
+
runOrThrow(spawn("secret-tool", [
|
|
3739
|
+
"store",
|
|
3740
|
+
"--label=Topolo external agent installation",
|
|
3741
|
+
"service",
|
|
3742
|
+
KEYCHAIN_SERVICE,
|
|
3743
|
+
"account",
|
|
3744
|
+
account
|
|
3745
|
+
], { input: value, encoding: "utf8" }), "Linux Secret Service write");
|
|
3746
|
+
},
|
|
3747
|
+
async get(account) {
|
|
3748
|
+
const result = spawn("secret-tool", [
|
|
3749
|
+
"lookup",
|
|
3750
|
+
"service",
|
|
3751
|
+
KEYCHAIN_SERVICE,
|
|
3752
|
+
"account",
|
|
3753
|
+
account
|
|
3754
|
+
], { encoding: "utf8" });
|
|
3755
|
+
if (result.status === 1) return null;
|
|
3756
|
+
return runOrThrow(result, "Linux Secret Service read");
|
|
3757
|
+
},
|
|
3758
|
+
async delete(account) {
|
|
3759
|
+
const result = spawn("secret-tool", [
|
|
3760
|
+
"clear",
|
|
3761
|
+
"service",
|
|
3762
|
+
KEYCHAIN_SERVICE,
|
|
3763
|
+
"account",
|
|
3764
|
+
account
|
|
3765
|
+
], { encoding: "utf8" });
|
|
3766
|
+
if (result.status !== 1) runOrThrow(result, "Linux Secret Service delete");
|
|
3767
|
+
}
|
|
3768
|
+
};
|
|
3769
|
+
}
|
|
3770
|
+
if (platform2 === "win32") {
|
|
3771
|
+
const secretPath = (account) => join(root, ".secrets", `${account.replace(":", "-")}.bin`);
|
|
3772
|
+
const powershell = (script, path, input) => spawn(
|
|
3773
|
+
"powershell.exe",
|
|
3774
|
+
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
3775
|
+
{ input, encoding: "utf8", env: { ...process.env, TOPOLO_SECRET_PATH: path } }
|
|
3776
|
+
);
|
|
3777
|
+
return {
|
|
3778
|
+
async set(account, value) {
|
|
3779
|
+
const path = secretPath(account);
|
|
3780
|
+
await mkdir(dirname(path), { recursive: true });
|
|
3781
|
+
const script = "$p=$env:TOPOLO_SECRET_PATH;$v=[Console]::In.ReadToEnd();$b=[Text.Encoding]::UTF8.GetBytes($v);$e=[Security.Cryptography.ProtectedData]::Protect($b,$null,[Security.Cryptography.DataProtectionScope]::CurrentUser);[IO.File]::WriteAllBytes($p,$e)";
|
|
3782
|
+
runOrThrow(powershell(script, path, value), "Windows DPAPI write");
|
|
3783
|
+
},
|
|
3784
|
+
async get(account) {
|
|
3785
|
+
const path = secretPath(account);
|
|
3786
|
+
if (!existsSync(path)) return null;
|
|
3787
|
+
const script = "$p=$env:TOPOLO_SECRET_PATH;$b=[IO.File]::ReadAllBytes($p);$d=[Security.Cryptography.ProtectedData]::Unprotect($b,$null,[Security.Cryptography.DataProtectionScope]::CurrentUser);[Console]::Out.Write([Text.Encoding]::UTF8.GetString($d))";
|
|
3788
|
+
return runOrThrow(powershell(script, path), "Windows DPAPI read");
|
|
3789
|
+
},
|
|
3790
|
+
async delete(account) {
|
|
3791
|
+
await rm(secretPath(account), { force: true });
|
|
3792
|
+
}
|
|
3793
|
+
};
|
|
3794
|
+
}
|
|
3795
|
+
throw new Error(`Secure external-agent installation storage is unsupported on ${platform2}.`);
|
|
3796
|
+
}
|
|
3797
|
+
async function readExternalAgentInstallation(environment, id, backend = createPlatformInstallationSecretBackend()) {
|
|
3798
|
+
const path = installationPath(environment, id);
|
|
3799
|
+
if (!existsSync(path)) return null;
|
|
3800
|
+
const installation = JSON.parse(await readFile(path, "utf8"));
|
|
3801
|
+
const encodedSecret = await backend.get(accountName(environment, id));
|
|
3802
|
+
if (!encodedSecret) throw new Error(`Protected secret is missing for installation ${id}.`);
|
|
3803
|
+
return { installation, secret: JSON.parse(encodedSecret) };
|
|
3804
|
+
}
|
|
2814
3805
|
var ENVIRONMENT_DEFAULT_SERVICE_URLS = TOPOLO_ENVIRONMENT_SERVICE_URLS;
|
|
2815
3806
|
function configPath() {
|
|
2816
3807
|
const override = process.env.TOPOLO_CONFIG_PATH;
|
|
2817
3808
|
if (override) return override;
|
|
2818
|
-
const base = process.env.XDG_CONFIG_HOME || (platform() === "win32" ?
|
|
2819
|
-
return
|
|
3809
|
+
const base = process.env.XDG_CONFIG_HOME || (platform() === "win32" ? join2(homedir2(), "AppData", "Roaming") : join2(homedir2(), ".config"));
|
|
3810
|
+
return join2(base, "topolo", "config.json");
|
|
2820
3811
|
}
|
|
2821
3812
|
async function readConfig() {
|
|
2822
3813
|
const path = configPath();
|
|
2823
|
-
if (!
|
|
3814
|
+
if (!existsSync2(path)) return {};
|
|
2824
3815
|
try {
|
|
2825
|
-
const raw = await
|
|
3816
|
+
const raw = await readFile2(path, "utf8");
|
|
2826
3817
|
return JSON.parse(raw);
|
|
2827
3818
|
} catch {
|
|
2828
3819
|
return {};
|
|
@@ -2830,11 +3821,11 @@ async function readConfig() {
|
|
|
2830
3821
|
}
|
|
2831
3822
|
async function writeConfig(config) {
|
|
2832
3823
|
const path = configPath();
|
|
2833
|
-
await
|
|
2834
|
-
await
|
|
3824
|
+
await mkdir2(dirname2(path), { recursive: true });
|
|
3825
|
+
await writeFile2(path, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
2835
3826
|
if (platform() !== "win32") {
|
|
2836
3827
|
try {
|
|
2837
|
-
await
|
|
3828
|
+
await chmod2(path, 384);
|
|
2838
3829
|
} catch {
|
|
2839
3830
|
}
|
|
2840
3831
|
}
|
|
@@ -2905,18 +3896,18 @@ var CONFIG_LOCK_TIMEOUT_MS = 5e3;
|
|
|
2905
3896
|
var CONFIG_LOCK_STALE_MS = 12e4;
|
|
2906
3897
|
async function withConfigWriteLock(operation) {
|
|
2907
3898
|
const lockPath = `${configPath()}.lock`;
|
|
2908
|
-
await
|
|
3899
|
+
await mkdir2(dirname2(lockPath), { recursive: true });
|
|
2909
3900
|
const startedAt = Date.now();
|
|
2910
3901
|
while (true) {
|
|
2911
3902
|
try {
|
|
2912
|
-
await
|
|
3903
|
+
await mkdir2(lockPath);
|
|
2913
3904
|
break;
|
|
2914
3905
|
} catch (error) {
|
|
2915
3906
|
if (error.code !== "EEXIST") throw error;
|
|
2916
3907
|
try {
|
|
2917
3908
|
const lock = await stat(lockPath);
|
|
2918
3909
|
if (Date.now() - lock.mtimeMs > CONFIG_LOCK_STALE_MS) {
|
|
2919
|
-
await
|
|
3910
|
+
await rm2(lockPath, { recursive: true, force: true });
|
|
2920
3911
|
continue;
|
|
2921
3912
|
}
|
|
2922
3913
|
} catch (inspectionError) {
|
|
@@ -2932,7 +3923,7 @@ async function withConfigWriteLock(operation) {
|
|
|
2932
3923
|
try {
|
|
2933
3924
|
return await operation();
|
|
2934
3925
|
} finally {
|
|
2935
|
-
await
|
|
3926
|
+
await rm2(lockPath, { recursive: true, force: true });
|
|
2936
3927
|
}
|
|
2937
3928
|
}
|
|
2938
3929
|
async function refreshCredentialIfNeeded(environment = selectedEnvironment(), options = {}) {
|
|
@@ -3770,14 +4761,14 @@ function displayAppId(entry) {
|
|
|
3770
4761
|
|
|
3771
4762
|
// src/version.ts
|
|
3772
4763
|
import { readFileSync } from "fs";
|
|
3773
|
-
import { dirname as
|
|
4764
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
3774
4765
|
import { fileURLToPath } from "url";
|
|
3775
4766
|
function readMcpVersion() {
|
|
3776
4767
|
try {
|
|
3777
|
-
const here =
|
|
4768
|
+
const here = dirname3(fileURLToPath(import.meta.url));
|
|
3778
4769
|
const candidates = [
|
|
3779
|
-
|
|
3780
|
-
|
|
4770
|
+
join3(here, "package.json"),
|
|
4771
|
+
join3(here, "..", "package.json")
|
|
3781
4772
|
];
|
|
3782
4773
|
for (const path of candidates) {
|
|
3783
4774
|
try {
|
|
@@ -3899,6 +4890,9 @@ function toolError(context, error) {
|
|
|
3899
4890
|
};
|
|
3900
4891
|
}
|
|
3901
4892
|
|
|
4893
|
+
// src/context-credential.ts
|
|
4894
|
+
import { randomUUID } from "crypto";
|
|
4895
|
+
|
|
3902
4896
|
// src/auth.ts
|
|
3903
4897
|
function resolveCredentialFromEnv(environment = "production") {
|
|
3904
4898
|
const prefix = environment === "production" ? "TOPOLO_PRODUCTION" : `TOPOLO_${environment.toUpperCase()}`;
|
|
@@ -3909,12 +4903,102 @@ function resolveCredentialFromEnv(environment = "production") {
|
|
|
3909
4903
|
return null;
|
|
3910
4904
|
}
|
|
3911
4905
|
|
|
4906
|
+
// src/installation-credential.ts
|
|
4907
|
+
function audienceFor(context) {
|
|
4908
|
+
const actionAudience = context.actionId?.match(/^(app_[A-Za-z0-9_]+)\./)?.[1];
|
|
4909
|
+
if (actionAudience) return actionAudience;
|
|
4910
|
+
if (context.actionId) {
|
|
4911
|
+
throw new TopoloMcpPublicError("The requested Topolo action has an invalid audience.");
|
|
4912
|
+
}
|
|
4913
|
+
const service = context.service.trim().toLowerCase();
|
|
4914
|
+
if (/^app_[a-z0-9_]+$/.test(service)) return service;
|
|
4915
|
+
if (!/^[a-z0-9_-]+$/.test(service)) {
|
|
4916
|
+
throw new TopoloMcpPublicError("The requested Topolo service cannot be bound to this installation.");
|
|
4917
|
+
}
|
|
4918
|
+
return `app_topolo_${service.replace(/-/g, "_")}`;
|
|
4919
|
+
}
|
|
4920
|
+
function responseData(value) {
|
|
4921
|
+
if (!value || typeof value !== "object") return {};
|
|
4922
|
+
const record2 = value;
|
|
4923
|
+
return record2.data && typeof record2.data === "object" ? record2.data : record2;
|
|
4924
|
+
}
|
|
4925
|
+
async function resolveExternalAgentInstallationCredential(environment, installationId, taskId, options = {}) {
|
|
4926
|
+
const stored = await (options.readInstallation ?? readExternalAgentInstallation)(environment, installationId);
|
|
4927
|
+
if (!stored) throw new TopoloMcpPublicError(`Installation ${installationId} is not registered on this device.`);
|
|
4928
|
+
if (stored.installation.id !== installationId || stored.installation.environment !== environment) {
|
|
4929
|
+
throw new TopoloMcpPublicError(`Installation ${installationId} is not registered for ${environment}.`);
|
|
4930
|
+
}
|
|
4931
|
+
if (!stored.installation.clientId.trim() || !stored.installation.organizationId.trim() || !/^[A-Za-z0-9_-]{43}$/.test(stored.installation.dpopJkt) || !stored.secret.clientSecret.trim() || !stored.secret.privateJwk.d) {
|
|
4932
|
+
throw new TopoloMcpPublicError(`Installation ${installationId} is incomplete or corrupted.`);
|
|
4933
|
+
}
|
|
4934
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
4935
|
+
const now = options.now ?? Date.now;
|
|
4936
|
+
const authBaseUrl = resolveServiceUrl("auth", options.serviceUrls);
|
|
4937
|
+
const tokenUrl = new URL("/api/developer-oauth/token", authBaseUrl).toString();
|
|
4938
|
+
const cache = /* @__PURE__ */ new Map();
|
|
4939
|
+
const credentialProvider = async (context) => {
|
|
4940
|
+
const audience = audienceFor(context);
|
|
4941
|
+
const resource = context.resource ?? {
|
|
4942
|
+
resourceType: "organization",
|
|
4943
|
+
resourceId: stored.installation.organizationId
|
|
4944
|
+
};
|
|
4945
|
+
const boundTaskId = context.taskId?.trim() || taskId;
|
|
4946
|
+
const key = JSON.stringify({ audience, taskId: boundTaskId, resource });
|
|
4947
|
+
const cached = cache.get(key);
|
|
4948
|
+
if (cached && cached.expiresAt - 15e3 > now()) return cached.credential;
|
|
4949
|
+
const body = new URLSearchParams({
|
|
4950
|
+
grant_type: "client_credentials",
|
|
4951
|
+
client_id: stored.installation.clientId,
|
|
4952
|
+
client_secret: stored.secret.clientSecret,
|
|
4953
|
+
audience,
|
|
4954
|
+
task_id: boundTaskId,
|
|
4955
|
+
resource_type: resource.resourceType,
|
|
4956
|
+
resource_id: resource.resourceId,
|
|
4957
|
+
dpop_jkt: stored.installation.dpopJkt
|
|
4958
|
+
});
|
|
4959
|
+
const response = await fetchImpl(tokenUrl, {
|
|
4960
|
+
method: "POST",
|
|
4961
|
+
headers: {
|
|
4962
|
+
Accept: "application/json",
|
|
4963
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
4964
|
+
},
|
|
4965
|
+
body: body.toString()
|
|
4966
|
+
});
|
|
4967
|
+
const payload = await response.json().catch(() => null);
|
|
4968
|
+
const data = responseData(payload);
|
|
4969
|
+
const accessToken = typeof data.access_token === "string" ? data.access_token : "";
|
|
4970
|
+
const expiresIn = Number(data.expires_in);
|
|
4971
|
+
if (!response.ok || !accessToken || !Number.isFinite(expiresIn) || expiresIn <= 0) {
|
|
4972
|
+
throw new TopoloMcpPublicError("The external-agent installation credential was rejected.");
|
|
4973
|
+
}
|
|
4974
|
+
const credential2 = {
|
|
4975
|
+
kind: "dpop_access_token",
|
|
4976
|
+
accessToken,
|
|
4977
|
+
privateJwk: stored.secret.privateJwk
|
|
4978
|
+
};
|
|
4979
|
+
cache.set(key, { credential: credential2, expiresAt: now() + expiresIn * 1e3 });
|
|
4980
|
+
return credential2;
|
|
4981
|
+
};
|
|
4982
|
+
const credential = await credentialProvider({
|
|
4983
|
+
service: "auth",
|
|
4984
|
+
method: "GET",
|
|
4985
|
+
path: "/api/auth/me",
|
|
4986
|
+
taskId,
|
|
4987
|
+
resource: {
|
|
4988
|
+
resourceType: "organization",
|
|
4989
|
+
resourceId: stored.installation.organizationId
|
|
4990
|
+
}
|
|
4991
|
+
});
|
|
4992
|
+
return { credential, credentialProvider, installation: stored.installation, taskId };
|
|
4993
|
+
}
|
|
4994
|
+
|
|
3912
4995
|
// src/context-credential.ts
|
|
3913
4996
|
var DEFAULT_DEPENDENCIES = {
|
|
3914
4997
|
readConfig,
|
|
3915
4998
|
refreshContextCredentialIfNeeded,
|
|
3916
4999
|
refreshCredentialIfNeeded,
|
|
3917
|
-
resolveCredentialFromEnv
|
|
5000
|
+
resolveCredentialFromEnv,
|
|
5001
|
+
resolveInstallationCredential: resolveExternalAgentInstallationCredential
|
|
3918
5002
|
};
|
|
3919
5003
|
function resolvePinnedContextKey(profile, selector) {
|
|
3920
5004
|
const normalized = selector.trim().toLowerCase();
|
|
@@ -3935,9 +5019,35 @@ function resolvePinnedContextKey(profile, selector) {
|
|
|
3935
5019
|
}
|
|
3936
5020
|
throw new TopoloMcpPublicError(`TOPOLO_CONTEXT "${selector}" is not signed in for this environment`);
|
|
3937
5021
|
}
|
|
3938
|
-
async function resolveMcpCredential(environment, selector = process.env.TOPOLO_CONTEXT, dependencies = DEFAULT_DEPENDENCIES) {
|
|
5022
|
+
async function resolveMcpCredential(environment, selector = process.env.TOPOLO_CONTEXT, dependencies = DEFAULT_DEPENDENCIES, installationSelector = process.env.TOPOLO_AGENT_INSTALLATION_ID, taskSelector = process.env.TOPOLO_AGENT_TASK_ID) {
|
|
3939
5023
|
const environmentCredential = dependencies.resolveCredentialFromEnv(environment);
|
|
3940
5024
|
const requestedSelector = selector?.trim();
|
|
5025
|
+
const installationId = installationSelector?.trim();
|
|
5026
|
+
if (installationId) {
|
|
5027
|
+
if (requestedSelector || environmentCredential) {
|
|
5028
|
+
throw new TopoloMcpPublicError(
|
|
5029
|
+
"TOPOLO_AGENT_INSTALLATION_ID cannot be combined with TOPOLO_CONTEXT or an environment credential"
|
|
5030
|
+
);
|
|
5031
|
+
}
|
|
5032
|
+
const taskId = taskSelector?.trim() || `task_${randomUUID()}`;
|
|
5033
|
+
const config2 = await dependencies.readConfig();
|
|
5034
|
+
const serviceUrls = resolveServiceUrls(
|
|
5035
|
+
profileForEnvironment(config2, environment).serviceUrls,
|
|
5036
|
+
environment
|
|
5037
|
+
);
|
|
5038
|
+
const resolved = await dependencies.resolveInstallationCredential(
|
|
5039
|
+
environment,
|
|
5040
|
+
installationId,
|
|
5041
|
+
taskId,
|
|
5042
|
+
{ serviceUrls }
|
|
5043
|
+
);
|
|
5044
|
+
return {
|
|
5045
|
+
credential: resolved.credential,
|
|
5046
|
+
contextKey: null,
|
|
5047
|
+
credentialProvider: resolved.credentialProvider,
|
|
5048
|
+
taskId: resolved.taskId
|
|
5049
|
+
};
|
|
5050
|
+
}
|
|
3941
5051
|
if (environmentCredential) {
|
|
3942
5052
|
if (requestedSelector) {
|
|
3943
5053
|
throw new TopoloMcpPublicError("TOPOLO_CONTEXT cannot be combined with an environment credential");
|
|
@@ -4040,6 +5150,10 @@ async function runStdioMcp(dependencies = DEFAULT_DEPENDENCIES2) {
|
|
|
4040
5150
|
const topolo = dependencies.createTopolo({
|
|
4041
5151
|
credential,
|
|
4042
5152
|
environment,
|
|
5153
|
+
actorContext: {
|
|
5154
|
+
channel: "mcp",
|
|
5155
|
+
...resolvedCredential.taskId ? { taskId: resolvedCredential.taskId } : {}
|
|
5156
|
+
},
|
|
4043
5157
|
agent: {
|
|
4044
5158
|
clientName: "topolo-mcp",
|
|
4045
5159
|
clientVersion: MCP_VERSION,
|