@topolo/cli 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +391 -85
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { platform as platform2 } from "os";
|
|
|
9
9
|
import { createInterface } from "readline/promises";
|
|
10
10
|
import { stdin as input } from "process";
|
|
11
11
|
|
|
12
|
-
// node_modules/.pnpm/@topolo+sdk@0.
|
|
12
|
+
// node_modules/.pnpm/@topolo+sdk@0.17.1/node_modules/@topolo/sdk/dist/index.js
|
|
13
13
|
function applyAuthHeaders(headers, credential) {
|
|
14
14
|
if (credential.kind === "api_key") {
|
|
15
15
|
headers.set("X-Api-Key", credential.apiKey);
|
|
@@ -173,10 +173,10 @@ var TopoloPermissionError = class extends TopoloSdkError {
|
|
|
173
173
|
};
|
|
174
174
|
function extractTopoloServiceErrorCode(body) {
|
|
175
175
|
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
176
|
-
const
|
|
177
|
-
const topLevel = canonicalTopoloErrorCode(
|
|
176
|
+
const record2 = body;
|
|
177
|
+
const topLevel = canonicalTopoloErrorCode(record2.code);
|
|
178
178
|
if (topLevel) return topLevel;
|
|
179
|
-
const nested =
|
|
179
|
+
const nested = record2.error;
|
|
180
180
|
const nestedString = canonicalTopoloErrorCode(nested);
|
|
181
181
|
if (nestedString) return nestedString;
|
|
182
182
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
@@ -191,14 +191,14 @@ function serviceErrorCode(body, fallback) {
|
|
|
191
191
|
}
|
|
192
192
|
function extractTopoloServiceErrorDetails(body) {
|
|
193
193
|
if (!body || typeof body !== "object" || Array.isArray(body)) return void 0;
|
|
194
|
-
const
|
|
195
|
-
let candidate =
|
|
196
|
-
const nested =
|
|
194
|
+
const record2 = body;
|
|
195
|
+
let candidate = record2.details;
|
|
196
|
+
const nested = record2.error;
|
|
197
197
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
198
198
|
const nestedRecord = nested;
|
|
199
199
|
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
200
200
|
}
|
|
201
|
-
if (candidate === void 0) candidate =
|
|
201
|
+
if (candidate === void 0) candidate = record2;
|
|
202
202
|
if (Array.isArray(candidate)) {
|
|
203
203
|
const paths = canonicalTopoloValidationPaths(candidate);
|
|
204
204
|
return paths.length ? { paths } : void 0;
|
|
@@ -250,8 +250,8 @@ function canonicalTopoloValidationPaths(issues) {
|
|
|
250
250
|
const paths = [];
|
|
251
251
|
for (const issue of issues) {
|
|
252
252
|
if (!issue || typeof issue !== "object" || Array.isArray(issue)) continue;
|
|
253
|
-
const
|
|
254
|
-
const path = canonicalTopoloValidationPath(
|
|
253
|
+
const record2 = issue;
|
|
254
|
+
const path = canonicalTopoloValidationPath(record2.field) ?? canonicalTopoloValidationPath(record2.path);
|
|
255
255
|
if (path && !paths.includes(path)) paths.push(path);
|
|
256
256
|
if (paths.length === 32) break;
|
|
257
257
|
}
|
|
@@ -718,6 +718,222 @@ function isMultipleOf(value, divisor) {
|
|
|
718
718
|
const quotient = value / divisor;
|
|
719
719
|
return Math.abs(quotient - Math.round(quotient)) < Number.EPSILON * Math.max(1, Math.abs(quotient)) * 4;
|
|
720
720
|
}
|
|
721
|
+
var ACTOR_ENVELOPE_VERSION = 1;
|
|
722
|
+
var TOPOLO_ACTOR_ENVELOPE_HEADER = "X-Topolo-Actor-Envelope";
|
|
723
|
+
var PRINCIPAL_TYPES = /* @__PURE__ */ new Set([
|
|
724
|
+
"human",
|
|
725
|
+
"managed_agent",
|
|
726
|
+
"external_agent",
|
|
727
|
+
"service",
|
|
728
|
+
"browser_automation"
|
|
729
|
+
]);
|
|
730
|
+
var ACCOUNTABLE_SUBJECT_TYPES = /* @__PURE__ */ new Set(["human", "organization"]);
|
|
731
|
+
var CHANNELS = /* @__PURE__ */ new Set(["web", "mobile", "cli", "mcp", "api", "workflow", "browser_automation"]);
|
|
732
|
+
var ASSURANCE_LEVELS = /* @__PURE__ */ new Set(["authenticated", "workload_identity", "user_verified"]);
|
|
733
|
+
var HASH_PATTERN = /^sha256:[a-f0-9]{64}$/;
|
|
734
|
+
var ActorEnvelopeValidationError = class extends Error {
|
|
735
|
+
path;
|
|
736
|
+
constructor(path, message2) {
|
|
737
|
+
super(`${path}: ${message2}`);
|
|
738
|
+
this.name = "ActorEnvelopeValidationError";
|
|
739
|
+
this.path = path;
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
function parseActorEnvelope(value) {
|
|
743
|
+
const envelope = record(value, "actorEnvelope");
|
|
744
|
+
exactKeys(envelope, [
|
|
745
|
+
"schemaVersion",
|
|
746
|
+
"organizationId",
|
|
747
|
+
"accountableSubject",
|
|
748
|
+
"performer",
|
|
749
|
+
"delegation",
|
|
750
|
+
"channel",
|
|
751
|
+
"assurance",
|
|
752
|
+
"action",
|
|
753
|
+
"issuedAt",
|
|
754
|
+
"expiresAt"
|
|
755
|
+
], "actorEnvelope");
|
|
756
|
+
if (envelope.schemaVersion !== ACTOR_ENVELOPE_VERSION) {
|
|
757
|
+
fail("actorEnvelope.schemaVersion", `must equal ${ACTOR_ENVELOPE_VERSION}`);
|
|
758
|
+
}
|
|
759
|
+
const accountableSubject = parseAccountableSubject(envelope.accountableSubject);
|
|
760
|
+
const performer = parseIdentity(envelope.performer, "actorEnvelope.performer");
|
|
761
|
+
const organizationId = text(envelope.organizationId, "actorEnvelope.organizationId");
|
|
762
|
+
const channel = member(envelope.channel, CHANNELS, "actorEnvelope.channel");
|
|
763
|
+
const issuedAt = timestamp(envelope.issuedAt, "actorEnvelope.issuedAt");
|
|
764
|
+
const expiresAt = envelope.expiresAt === void 0 ? void 0 : timestamp(envelope.expiresAt, "actorEnvelope.expiresAt");
|
|
765
|
+
if (expiresAt && Date.parse(expiresAt) <= Date.parse(issuedAt)) {
|
|
766
|
+
fail("actorEnvelope.expiresAt", "must be later than issuedAt");
|
|
767
|
+
}
|
|
768
|
+
const assuranceValue = record(envelope.assurance, "actorEnvelope.assurance");
|
|
769
|
+
exactKeys(assuranceValue, ["level", "methods"], "actorEnvelope.assurance");
|
|
770
|
+
const assurance = {
|
|
771
|
+
level: member(assuranceValue.level, ASSURANCE_LEVELS, "actorEnvelope.assurance.level"),
|
|
772
|
+
methods: textArray(assuranceValue.methods, "actorEnvelope.assurance.methods")
|
|
773
|
+
};
|
|
774
|
+
const actionValue = record(envelope.action, "actorEnvelope.action");
|
|
775
|
+
exactKeys(actionValue, ["requestId", "actionId", "resourceType", "resourceId", "inputHash"], "actorEnvelope.action");
|
|
776
|
+
const inputHash = text(actionValue.inputHash, "actorEnvelope.action.inputHash");
|
|
777
|
+
if (!HASH_PATTERN.test(inputHash)) fail("actorEnvelope.action.inputHash", "must be a lowercase sha256 digest");
|
|
778
|
+
const action = {
|
|
779
|
+
requestId: text(actionValue.requestId, "actorEnvelope.action.requestId"),
|
|
780
|
+
actionId: text(actionValue.actionId, "actorEnvelope.action.actionId"),
|
|
781
|
+
...actionValue.resourceType === void 0 ? {} : { resourceType: text(actionValue.resourceType, "actorEnvelope.action.resourceType") },
|
|
782
|
+
...actionValue.resourceId === void 0 ? {} : { resourceId: text(actionValue.resourceId, "actorEnvelope.action.resourceId") },
|
|
783
|
+
inputHash
|
|
784
|
+
};
|
|
785
|
+
if (action.resourceType === void 0 !== (action.resourceId === void 0)) {
|
|
786
|
+
fail("actorEnvelope.action", "resourceType and resourceId must be provided together");
|
|
787
|
+
}
|
|
788
|
+
const delegation = parseDelegation(envelope.delegation);
|
|
789
|
+
const directHuman = performer.principalType === "human" && performer.principalId === accountableSubject.principalId;
|
|
790
|
+
if (directHuman && delegation !== null) fail("actorEnvelope.delegation", "must be null for direct human action");
|
|
791
|
+
if (!directHuman && delegation === null) fail("actorEnvelope.delegation", "is required when performer differs from accountable subject");
|
|
792
|
+
if (delegation) {
|
|
793
|
+
const finalHop = delegation.lineage.at(-1);
|
|
794
|
+
if (!finalHop || finalHop.principalId !== performer.principalId || finalHop.principalType !== performer.principalType) {
|
|
795
|
+
fail("actorEnvelope.delegation.lineage", "must end with the performer");
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if (performer.principalType === "external_agent" && (!performer.installationId || !performer.provider)) {
|
|
799
|
+
fail("actorEnvelope.performer", "external_agent requires installationId and provider");
|
|
800
|
+
}
|
|
801
|
+
if (performer.principalType === "managed_agent" && !performer.agentInstanceId) {
|
|
802
|
+
fail("actorEnvelope.performer.agentInstanceId", "is required for managed_agent");
|
|
803
|
+
}
|
|
804
|
+
if (performer.principalType === "browser_automation" && (!performer.installationId || !performer.browserSessionId)) {
|
|
805
|
+
fail("actorEnvelope.performer", "browser_automation requires installationId and browserSessionId");
|
|
806
|
+
}
|
|
807
|
+
return {
|
|
808
|
+
schemaVersion: ACTOR_ENVELOPE_VERSION,
|
|
809
|
+
organizationId,
|
|
810
|
+
accountableSubject,
|
|
811
|
+
performer,
|
|
812
|
+
delegation,
|
|
813
|
+
channel,
|
|
814
|
+
assurance,
|
|
815
|
+
action,
|
|
816
|
+
issuedAt,
|
|
817
|
+
...expiresAt ? { expiresAt } : {}
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
function canonicalizeActorEnvelope(value) {
|
|
821
|
+
return canonicalJson(parseActorEnvelope(value));
|
|
822
|
+
}
|
|
823
|
+
function encodeActorEnvelopeHeader(value) {
|
|
824
|
+
const bytes = new TextEncoder().encode(canonicalizeActorEnvelope(value));
|
|
825
|
+
const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
|
|
826
|
+
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
827
|
+
}
|
|
828
|
+
function canonicalJson(value) {
|
|
829
|
+
if (value === null) return "null";
|
|
830
|
+
if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
|
|
831
|
+
if (typeof value === "number") {
|
|
832
|
+
if (!Number.isFinite(value)) throw new TypeError("Canonical JSON accepts finite numbers only.");
|
|
833
|
+
return JSON.stringify(value);
|
|
834
|
+
}
|
|
835
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
836
|
+
if (typeof value !== "object" || !value || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
|
|
837
|
+
throw new TypeError("Canonical JSON accepts JSON objects, arrays, and primitives only.");
|
|
838
|
+
}
|
|
839
|
+
const entries = Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right));
|
|
840
|
+
return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
|
|
841
|
+
}
|
|
842
|
+
function parseAccountableSubject(value) {
|
|
843
|
+
const path = "actorEnvelope.accountableSubject";
|
|
844
|
+
const subject = record(value, path);
|
|
845
|
+
exactKeys(subject, ["principalType", "principalId", "displayName"], path);
|
|
846
|
+
return {
|
|
847
|
+
principalType: member(subject.principalType, ACCOUNTABLE_SUBJECT_TYPES, `${path}.principalType`),
|
|
848
|
+
principalId: text(subject.principalId, `${path}.principalId`),
|
|
849
|
+
...subject.displayName === void 0 ? {} : { displayName: text(subject.displayName, `${path}.displayName`) }
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
async function sha256CanonicalValue(value) {
|
|
853
|
+
const bytes = new TextEncoder().encode(canonicalJson(value));
|
|
854
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
855
|
+
return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
856
|
+
}
|
|
857
|
+
function parseIdentity(value, path) {
|
|
858
|
+
const identity = record(value, path);
|
|
859
|
+
exactKeys(identity, [
|
|
860
|
+
"principalType",
|
|
861
|
+
"principalId",
|
|
862
|
+
"displayName",
|
|
863
|
+
"installationId",
|
|
864
|
+
"agentInstanceId",
|
|
865
|
+
"browserSessionId",
|
|
866
|
+
"provider",
|
|
867
|
+
"model"
|
|
868
|
+
], path);
|
|
869
|
+
return {
|
|
870
|
+
principalType: member(identity.principalType, PRINCIPAL_TYPES, `${path}.principalType`),
|
|
871
|
+
principalId: text(identity.principalId, `${path}.principalId`),
|
|
872
|
+
...optionalTextFields(identity, path, [
|
|
873
|
+
"displayName",
|
|
874
|
+
"installationId",
|
|
875
|
+
"agentInstanceId",
|
|
876
|
+
"browserSessionId",
|
|
877
|
+
"provider",
|
|
878
|
+
"model"
|
|
879
|
+
])
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
function parseDelegation(value) {
|
|
883
|
+
if (value === null) return null;
|
|
884
|
+
const delegation = record(value, "actorEnvelope.delegation");
|
|
885
|
+
exactKeys(delegation, ["taskId", "runId", "lineage"], "actorEnvelope.delegation");
|
|
886
|
+
if (!Array.isArray(delegation.lineage) || delegation.lineage.length === 0 || delegation.lineage.length > 16) {
|
|
887
|
+
fail("actorEnvelope.delegation.lineage", "must contain between 1 and 16 hops");
|
|
888
|
+
}
|
|
889
|
+
return {
|
|
890
|
+
taskId: text(delegation.taskId, "actorEnvelope.delegation.taskId"),
|
|
891
|
+
runId: text(delegation.runId, "actorEnvelope.delegation.runId"),
|
|
892
|
+
lineage: delegation.lineage.map((value2, index) => {
|
|
893
|
+
const hop = record(value2, `actorEnvelope.delegation.lineage[${index}]`);
|
|
894
|
+
exactKeys(hop, ["principalType", "principalId"], `actorEnvelope.delegation.lineage[${index}]`);
|
|
895
|
+
return {
|
|
896
|
+
principalType: member(hop.principalType, PRINCIPAL_TYPES, `actorEnvelope.delegation.lineage[${index}].principalType`),
|
|
897
|
+
principalId: text(hop.principalId, `actorEnvelope.delegation.lineage[${index}].principalId`)
|
|
898
|
+
};
|
|
899
|
+
})
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
function record(value, path) {
|
|
903
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "must be an object");
|
|
904
|
+
return value;
|
|
905
|
+
}
|
|
906
|
+
function exactKeys(value, allowed, path) {
|
|
907
|
+
const unknown = Object.keys(value).filter((key) => !allowed.includes(key));
|
|
908
|
+
if (unknown.length) fail(path, `contains unknown field ${unknown[0]}`);
|
|
909
|
+
}
|
|
910
|
+
function text(value, path) {
|
|
911
|
+
if (typeof value !== "string" || value.trim() !== value || value.length === 0 || value.length > 256) {
|
|
912
|
+
fail(path, "must be a non-empty trimmed string of at most 256 characters");
|
|
913
|
+
}
|
|
914
|
+
return value;
|
|
915
|
+
}
|
|
916
|
+
function timestamp(value, path) {
|
|
917
|
+
const result = text(value, path);
|
|
918
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(result) || Number.isNaN(Date.parse(result))) {
|
|
919
|
+
fail(path, "must be an RFC 3339 UTC timestamp");
|
|
920
|
+
}
|
|
921
|
+
return result;
|
|
922
|
+
}
|
|
923
|
+
function member(value, values, path) {
|
|
924
|
+
if (typeof value !== "string" || !values.has(value)) fail(path, "contains an unsupported value");
|
|
925
|
+
return value;
|
|
926
|
+
}
|
|
927
|
+
function textArray(value, path) {
|
|
928
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > 16) fail(path, "must contain between 1 and 16 values");
|
|
929
|
+
return value.map((item, index) => text(item, `${path}[${index}]`));
|
|
930
|
+
}
|
|
931
|
+
function optionalTextFields(value, path, keys) {
|
|
932
|
+
return Object.fromEntries(keys.flatMap((key) => value[key] === void 0 ? [] : [[key, text(value[key], `${path}.${key}`)]]));
|
|
933
|
+
}
|
|
934
|
+
function fail(path, message2) {
|
|
935
|
+
throw new ActorEnvelopeValidationError(path, message2);
|
|
936
|
+
}
|
|
721
937
|
function parseTopoloActionUploadContract(value) {
|
|
722
938
|
if (!isRecord2(value) || value.profile !== "inline-base64" && value.profile !== "url-session") {
|
|
723
939
|
throw new TopoloSdkError("upload_contract_unavailable", "This action does not publish a supported upload contract.");
|
|
@@ -922,6 +1138,7 @@ var NEGATIVE_CACHE_TTL_MS = 60 * 1e3;
|
|
|
922
1138
|
var TopoloClient = class _TopoloClient {
|
|
923
1139
|
credential;
|
|
924
1140
|
agent;
|
|
1141
|
+
actorContext;
|
|
925
1142
|
serviceUrls;
|
|
926
1143
|
environment;
|
|
927
1144
|
appIds;
|
|
@@ -952,6 +1169,11 @@ var TopoloClient = class _TopoloClient {
|
|
|
952
1169
|
if (!options.agent?.clientName) throw new TopoloAuthError("agent.clientName is required");
|
|
953
1170
|
this.credential = options.credential;
|
|
954
1171
|
this.agent = options.agent;
|
|
1172
|
+
this.actorContext = options.actorContext ? {
|
|
1173
|
+
channel: options.actorContext.channel,
|
|
1174
|
+
taskId: options.actorContext.taskId ?? `task_${generateRequestId()}`,
|
|
1175
|
+
runId: options.actorContext.runId ?? `run_${generateRequestId()}`
|
|
1176
|
+
} : void 0;
|
|
955
1177
|
this.environment = options.environment;
|
|
956
1178
|
this.serviceUrls = options.environment ? topoloServiceOrigins(options.serviceUrls, options.environment) : options.serviceUrls;
|
|
957
1179
|
this.appIds = options.appIds;
|
|
@@ -1005,11 +1227,15 @@ var TopoloClient = class _TopoloClient {
|
|
|
1005
1227
|
const platformAppId = resolveConfiguredAppId(resolvedService.serviceKey, this.appIds);
|
|
1006
1228
|
if (platformAppId) headers.set("X-App-ID", platformAppId);
|
|
1007
1229
|
applyAuthHeaders(headers, this.credential);
|
|
1008
|
-
const
|
|
1230
|
+
const actorEnvelope = opts.actorEnvelope === void 0 ? void 0 : parseActorEnvelope(opts.actorEnvelope);
|
|
1231
|
+
const requestId = actorEnvelope?.action.requestId ?? generateRequestId();
|
|
1009
1232
|
applyAuditHeaders(headers, this.agent, requestId);
|
|
1010
1233
|
if (opts.headers) {
|
|
1011
1234
|
for (const [k, v] of Object.entries(opts.headers)) headers.set(k, v);
|
|
1012
1235
|
}
|
|
1236
|
+
if (actorEnvelope) {
|
|
1237
|
+
headers.set(TOPOLO_ACTOR_ENVELOPE_HEADER, encodeActorEnvelopeHeader(actorEnvelope));
|
|
1238
|
+
}
|
|
1013
1239
|
if (opts.resource) {
|
|
1014
1240
|
const resourceType = opts.resource.resourceType.trim();
|
|
1015
1241
|
const resourceId = opts.resource.resourceId.trim();
|
|
@@ -1456,14 +1682,14 @@ var TopoloClient = class _TopoloClient {
|
|
|
1456
1682
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
1457
1683
|
throw new TopoloSdkError("action_catalog_summary_unavailable", "Action catalog summary is unavailable.");
|
|
1458
1684
|
}
|
|
1459
|
-
const
|
|
1460
|
-
if (
|
|
1685
|
+
const record2 = raw;
|
|
1686
|
+
if (record2.algorithm !== "sha256-v1" || typeof record2.digest !== "string" || !/^[a-f0-9]{64}$/.test(record2.digest) || !Number.isSafeInteger(record2.actionCount) || Number(record2.actionCount) < 0) {
|
|
1461
1687
|
throw new TopoloSdkError("action_catalog_summary_unavailable", "Action catalog summary is invalid.");
|
|
1462
1688
|
}
|
|
1463
1689
|
return {
|
|
1464
1690
|
algorithm: "sha256-v1",
|
|
1465
|
-
digest:
|
|
1466
|
-
actionCount: Number(
|
|
1691
|
+
digest: record2.digest,
|
|
1692
|
+
actionCount: Number(record2.actionCount)
|
|
1467
1693
|
};
|
|
1468
1694
|
}
|
|
1469
1695
|
/** @deprecated Prefer searchActions() so callers retain the continuation cursor. */
|
|
@@ -1663,6 +1889,20 @@ var TopoloClient = class _TopoloClient {
|
|
|
1663
1889
|
);
|
|
1664
1890
|
}
|
|
1665
1891
|
const mapped = mapActionInput(entry, input2);
|
|
1892
|
+
const resolvedActorEnvelope = options.actorEnvelope ?? await this.buildVerifiedAgentActorEnvelope(entry.actionId, input2, options.resource);
|
|
1893
|
+
if (resolvedActorEnvelope) {
|
|
1894
|
+
const actorEnvelope = parseActorEnvelope(resolvedActorEnvelope);
|
|
1895
|
+
if (actorEnvelope.action.actionId !== entry.actionId) {
|
|
1896
|
+
throw new TopoloSdkError("actor_envelope_action_mismatch", "Actor envelope actionId does not match the executed action.");
|
|
1897
|
+
}
|
|
1898
|
+
if (actorEnvelope.action.inputHash !== await sha256CanonicalValue(input2)) {
|
|
1899
|
+
throw new TopoloSdkError("actor_envelope_input_mismatch", "Actor envelope inputHash does not match the executed action input.");
|
|
1900
|
+
}
|
|
1901
|
+
const envelopeResource = actorEnvelope.action.resourceType && actorEnvelope.action.resourceId ? { resourceType: actorEnvelope.action.resourceType, resourceId: actorEnvelope.action.resourceId } : void 0;
|
|
1902
|
+
if (envelopeResource?.resourceType !== options.resource?.resourceType || envelopeResource?.resourceId !== options.resource?.resourceId) {
|
|
1903
|
+
throw new TopoloSdkError("actor_envelope_resource_mismatch", "Actor envelope resource does not match the executed action resource.");
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1666
1906
|
const service = entry.appSlug || entry.appId;
|
|
1667
1907
|
const result = await this.request({
|
|
1668
1908
|
service,
|
|
@@ -1672,6 +1912,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1672
1912
|
...mapped.query ? { query: mapped.query } : {},
|
|
1673
1913
|
...mapped.body !== void 0 ? { body: mapped.body } : {},
|
|
1674
1914
|
...options.resource ? { resource: options.resource } : {},
|
|
1915
|
+
...resolvedActorEnvelope ? { actorEnvelope: resolvedActorEnvelope } : {},
|
|
1675
1916
|
headers: {
|
|
1676
1917
|
[TOPOLO_ACTION_ID_HEADER]: entry.actionId,
|
|
1677
1918
|
[TOPOLO_ACTION_NAME_HEADER]: entry.name
|
|
@@ -1680,6 +1921,56 @@ var TopoloClient = class _TopoloClient {
|
|
|
1680
1921
|
});
|
|
1681
1922
|
return redactActionResult(result);
|
|
1682
1923
|
}
|
|
1924
|
+
async buildVerifiedAgentActorEnvelope(actionId, input2, resource) {
|
|
1925
|
+
if (!this.actorContext) return void 0;
|
|
1926
|
+
const identity = await this.introspect();
|
|
1927
|
+
const principalType = identity.user.principalType;
|
|
1928
|
+
if (principalType !== "agent_employee" && principalType !== "external_agent") return void 0;
|
|
1929
|
+
if (!identity.organization || !identity.user.accountableUserId || !identity.user.principalId) {
|
|
1930
|
+
throw new TopoloSdkError(
|
|
1931
|
+
"incomplete_agent_identity",
|
|
1932
|
+
"Verified agent credentials must resolve an organization, accountable user, and principal id."
|
|
1933
|
+
);
|
|
1934
|
+
}
|
|
1935
|
+
const performer = principalType === "external_agent" ? {
|
|
1936
|
+
principalType: "external_agent",
|
|
1937
|
+
principalId: identity.user.principalId,
|
|
1938
|
+
installationId: identity.user.installationId ?? void 0,
|
|
1939
|
+
provider: identity.user.provider ?? void 0,
|
|
1940
|
+
model: identity.user.model ?? void 0
|
|
1941
|
+
} : {
|
|
1942
|
+
principalType: "managed_agent",
|
|
1943
|
+
principalId: identity.user.principalId,
|
|
1944
|
+
agentInstanceId: identity.user.agentInstanceId ?? void 0,
|
|
1945
|
+
model: identity.user.model ?? void 0
|
|
1946
|
+
};
|
|
1947
|
+
if (performer.principalType === "external_agent" && (!performer.installationId || !performer.provider) || performer.principalType === "managed_agent" && !performer.agentInstanceId) {
|
|
1948
|
+
throw new TopoloSdkError(
|
|
1949
|
+
"incomplete_agent_identity",
|
|
1950
|
+
"Verified agent credentials are missing the performer identity required for provenance."
|
|
1951
|
+
);
|
|
1952
|
+
}
|
|
1953
|
+
return parseActorEnvelope({
|
|
1954
|
+
schemaVersion: 1,
|
|
1955
|
+
organizationId: identity.organization.id,
|
|
1956
|
+
accountableSubject: { principalType: "human", principalId: identity.user.accountableUserId },
|
|
1957
|
+
performer,
|
|
1958
|
+
delegation: {
|
|
1959
|
+
taskId: this.actorContext.taskId,
|
|
1960
|
+
runId: this.actorContext.runId,
|
|
1961
|
+
lineage: [{ principalType: performer.principalType, principalId: performer.principalId }]
|
|
1962
|
+
},
|
|
1963
|
+
channel: this.actorContext.channel,
|
|
1964
|
+
assurance: { level: "workload_identity", methods: ["client_credentials"] },
|
|
1965
|
+
action: {
|
|
1966
|
+
requestId: generateRequestId(),
|
|
1967
|
+
actionId,
|
|
1968
|
+
...resource ? resource : {},
|
|
1969
|
+
inputHash: await sha256CanonicalValue(input2)
|
|
1970
|
+
},
|
|
1971
|
+
issuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1683
1974
|
/**
|
|
1684
1975
|
* Introspect the current credential. Returns the caller's resolved identity,
|
|
1685
1976
|
* organization, and permission set. Used by CLI `whoami` and by MCP to gate
|
|
@@ -1707,7 +1998,18 @@ var TopoloClient = class _TopoloClient {
|
|
|
1707
1998
|
email: user.email,
|
|
1708
1999
|
name: user.name ?? null,
|
|
1709
2000
|
role: user.role ?? (kind === "api_key" ? "service" : "member"),
|
|
1710
|
-
permissions: payload.permissions ?? user.permissions ?? []
|
|
2001
|
+
permissions: payload.permissions ?? user.permissions ?? [],
|
|
2002
|
+
...normalizeAgentPrincipalType(user.principalType) ? {
|
|
2003
|
+
principalId: readOptionalString(user.principalId),
|
|
2004
|
+
principalType: normalizeAgentPrincipalType(user.principalType),
|
|
2005
|
+
accountableUserId: readOptionalString(user.accountableUserId),
|
|
2006
|
+
agentInstanceId: readOptionalString(user.agentInstanceId),
|
|
2007
|
+
installationId: readOptionalString(user.installationId),
|
|
2008
|
+
provider: readOptionalString(user.provider),
|
|
2009
|
+
model: readOptionalString(user.model),
|
|
2010
|
+
serviceScopes: stringArray(user.serviceScopes),
|
|
2011
|
+
policyRef: readOptionalString(user.policyRef)
|
|
2012
|
+
} : {}
|
|
1711
2013
|
},
|
|
1712
2014
|
organization: organization ? {
|
|
1713
2015
|
id: organization.id,
|
|
@@ -2072,6 +2374,9 @@ function normalizeCredentialAuthorizationMode(value, kind) {
|
|
|
2072
2374
|
if (value === "context_rbac" || value === "oauth_scopes" || value === "session_rbac") return value;
|
|
2073
2375
|
return "unknown";
|
|
2074
2376
|
}
|
|
2377
|
+
function normalizeAgentPrincipalType(value) {
|
|
2378
|
+
return value === "agent_employee" || value === "external_agent" ? value : null;
|
|
2379
|
+
}
|
|
2075
2380
|
function describeError(body, fallback) {
|
|
2076
2381
|
if (body && typeof body === "object") {
|
|
2077
2382
|
const maybe = body;
|
|
@@ -2096,10 +2401,10 @@ function extractRequired(body) {
|
|
|
2096
2401
|
if (Array.isArray(maybe.required)) return canonicalTopoloRequired(maybe.required);
|
|
2097
2402
|
for (const nested of [maybe.details, maybe.error]) {
|
|
2098
2403
|
if (!nested || typeof nested !== "object" || Array.isArray(nested)) continue;
|
|
2099
|
-
const
|
|
2100
|
-
if (Array.isArray(
|
|
2101
|
-
if (
|
|
2102
|
-
const required =
|
|
2404
|
+
const record2 = nested;
|
|
2405
|
+
if (Array.isArray(record2.required)) return canonicalTopoloRequired(record2.required);
|
|
2406
|
+
if (record2.details && typeof record2.details === "object" && !Array.isArray(record2.details)) {
|
|
2407
|
+
const required = record2.details.required;
|
|
2103
2408
|
if (Array.isArray(required)) return canonicalTopoloRequired(required);
|
|
2104
2409
|
}
|
|
2105
2410
|
}
|
|
@@ -2174,8 +2479,8 @@ function compactResultValue(value, depth, budget) {
|
|
|
2174
2479
|
return value.length > ACTION_RESULT_MAX_ITEMS ? { count: value.length, items, omittedItems: value.length - ACTION_RESULT_MAX_ITEMS } : items;
|
|
2175
2480
|
}
|
|
2176
2481
|
if (typeof value === "object") {
|
|
2177
|
-
const
|
|
2178
|
-
const allKeys = Object.keys(
|
|
2482
|
+
const record2 = value;
|
|
2483
|
+
const allKeys = Object.keys(record2);
|
|
2179
2484
|
const ranked = allKeys.sort((left, right) => resultKeyPriority(left) - resultKeyPriority(right));
|
|
2180
2485
|
const prioritized = ranked.filter((key) => resultKeyPriority(key) < 2);
|
|
2181
2486
|
const ordinary = ranked.filter((key) => resultKeyPriority(key) === 2);
|
|
@@ -2184,7 +2489,7 @@ function compactResultValue(value, depth, budget) {
|
|
|
2184
2489
|
...prioritized,
|
|
2185
2490
|
...ordinary.slice(0, ordinaryLimit)
|
|
2186
2491
|
].slice(0, ACTION_RESULT_MAX_KEYS);
|
|
2187
|
-
const compact = Object.fromEntries(keys.map((key) => [key, compactResultValue(
|
|
2492
|
+
const compact = Object.fromEntries(keys.map((key) => [key, compactResultValue(record2[key], depth + 1, budget)]));
|
|
2188
2493
|
if (allKeys.length > keys.length) compact["_omittedKeys"] = allKeys.length - keys.length;
|
|
2189
2494
|
return compact;
|
|
2190
2495
|
}
|
|
@@ -2948,7 +3253,7 @@ function createTopolo(options) {
|
|
|
2948
3253
|
};
|
|
2949
3254
|
}
|
|
2950
3255
|
|
|
2951
|
-
// node_modules/.pnpm/@topolo+sdk@0.
|
|
3256
|
+
// node_modules/.pnpm/@topolo+sdk@0.17.1/node_modules/@topolo/sdk/dist/node/credential-store.js
|
|
2952
3257
|
import { mkdir, readFile, writeFile, chmod, rm, stat } from "fs/promises";
|
|
2953
3258
|
import { existsSync } from "fs";
|
|
2954
3259
|
import { homedir, platform } from "os";
|
|
@@ -3071,10 +3376,10 @@ var TopoloAuthError2 = class extends TopoloSdkError2 {
|
|
|
3071
3376
|
};
|
|
3072
3377
|
function extractTopoloServiceErrorCode2(body) {
|
|
3073
3378
|
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
3074
|
-
const
|
|
3075
|
-
const topLevel = canonicalTopoloErrorCode2(
|
|
3379
|
+
const record2 = body;
|
|
3380
|
+
const topLevel = canonicalTopoloErrorCode2(record2.code);
|
|
3076
3381
|
if (topLevel) return topLevel;
|
|
3077
|
-
const nested =
|
|
3382
|
+
const nested = record2.error;
|
|
3078
3383
|
const nestedString = canonicalTopoloErrorCode2(nested);
|
|
3079
3384
|
if (nestedString) return nestedString;
|
|
3080
3385
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
@@ -3089,14 +3394,14 @@ function serviceErrorCode2(body, fallback) {
|
|
|
3089
3394
|
}
|
|
3090
3395
|
function extractTopoloServiceErrorDetails2(body) {
|
|
3091
3396
|
if (!body || typeof body !== "object" || Array.isArray(body)) return void 0;
|
|
3092
|
-
const
|
|
3093
|
-
let candidate =
|
|
3094
|
-
const nested =
|
|
3397
|
+
const record2 = body;
|
|
3398
|
+
let candidate = record2.details;
|
|
3399
|
+
const nested = record2.error;
|
|
3095
3400
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
3096
3401
|
const nestedRecord = nested;
|
|
3097
3402
|
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
3098
3403
|
}
|
|
3099
|
-
if (candidate === void 0) candidate =
|
|
3404
|
+
if (candidate === void 0) candidate = record2;
|
|
3100
3405
|
if (Array.isArray(candidate)) {
|
|
3101
3406
|
const paths = canonicalTopoloValidationPaths2(candidate);
|
|
3102
3407
|
return paths.length ? { paths } : void 0;
|
|
@@ -3148,8 +3453,8 @@ function canonicalTopoloValidationPaths2(issues) {
|
|
|
3148
3453
|
const paths = [];
|
|
3149
3454
|
for (const issue of issues) {
|
|
3150
3455
|
if (!issue || typeof issue !== "object" || Array.isArray(issue)) continue;
|
|
3151
|
-
const
|
|
3152
|
-
const path = canonicalTopoloValidationPath2(
|
|
3456
|
+
const record2 = issue;
|
|
3457
|
+
const path = canonicalTopoloValidationPath2(record2.field) ?? canonicalTopoloValidationPath2(record2.path);
|
|
3153
3458
|
if (path && !paths.includes(path)) paths.push(path);
|
|
3154
3459
|
if (paths.length === 32) break;
|
|
3155
3460
|
}
|
|
@@ -3725,7 +4030,7 @@ function installOutputErrorHandlers(stdout = process.stdout, stderr = process.st
|
|
|
3725
4030
|
stdout.on("error", handler);
|
|
3726
4031
|
stderr.on("error", handler);
|
|
3727
4032
|
}
|
|
3728
|
-
function
|
|
4033
|
+
function fail2(err, mode) {
|
|
3729
4034
|
const payload = normalizeError(err);
|
|
3730
4035
|
if (mode === "json") {
|
|
3731
4036
|
process.stderr.write(JSON.stringify({ error: payload }, null, 2) + "\n");
|
|
@@ -3834,7 +4139,7 @@ Visit ${apiKeyIssuanceUrl(environment)} to mint one, or configure an OAuth clien
|
|
|
3834
4139
|
openBrowser: opts.browser !== false
|
|
3835
4140
|
});
|
|
3836
4141
|
} catch (err) {
|
|
3837
|
-
|
|
4142
|
+
fail2(err, mode);
|
|
3838
4143
|
}
|
|
3839
4144
|
}
|
|
3840
4145
|
);
|
|
@@ -3850,7 +4155,7 @@ Visit ${apiKeyIssuanceUrl(environment)} to mint one, or configure an OAuth clien
|
|
|
3850
4155
|
() => `Logged out of Topolo ${environmentLabel(environment)}.`
|
|
3851
4156
|
);
|
|
3852
4157
|
} catch (err) {
|
|
3853
|
-
|
|
4158
|
+
fail2(err, mode);
|
|
3854
4159
|
}
|
|
3855
4160
|
});
|
|
3856
4161
|
auth.command("status").description("Show which credential source is in use (env vs config file)").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (opts) => {
|
|
@@ -3876,7 +4181,7 @@ Visit ${apiKeyIssuanceUrl(environment)} to mint one, or configure an OAuth clien
|
|
|
3876
4181
|
].join("\n")
|
|
3877
4182
|
);
|
|
3878
4183
|
} catch (err) {
|
|
3879
|
-
|
|
4184
|
+
fail2(err, mode);
|
|
3880
4185
|
}
|
|
3881
4186
|
});
|
|
3882
4187
|
}
|
|
@@ -4068,6 +4373,7 @@ async function buildClient(options) {
|
|
|
4068
4373
|
return createTopolo({
|
|
4069
4374
|
credential,
|
|
4070
4375
|
environment,
|
|
4376
|
+
actorContext: { channel: "cli" },
|
|
4071
4377
|
agent: {
|
|
4072
4378
|
clientName: "topolo-cli",
|
|
4073
4379
|
clientVersion: CLI_VERSION,
|
|
@@ -4125,7 +4431,7 @@ function registerWhoamiCommand(program2) {
|
|
|
4125
4431
|
const info = await topolo.identity.whoami();
|
|
4126
4432
|
emit(info, mode, () => formatPretty(info));
|
|
4127
4433
|
} catch (err) {
|
|
4128
|
-
|
|
4434
|
+
fail2(err, mode);
|
|
4129
4435
|
}
|
|
4130
4436
|
});
|
|
4131
4437
|
}
|
|
@@ -4206,7 +4512,7 @@ function registerApiCommand(program2) {
|
|
|
4206
4512
|
});
|
|
4207
4513
|
emit(result, mode, () => JSON.stringify(result, null, 2));
|
|
4208
4514
|
} catch (err) {
|
|
4209
|
-
|
|
4515
|
+
fail2(err, mode);
|
|
4210
4516
|
}
|
|
4211
4517
|
}
|
|
4212
4518
|
);
|
|
@@ -4270,7 +4576,7 @@ function registerActionsCommand(program2) {
|
|
|
4270
4576
|
const output = opts.detail ? { ...result, actions: actions2 } : { actions: actions2.map(compactActionCatalogEntry), page: result.page };
|
|
4271
4577
|
emit(output, mode, () => formatActionRows(actions2, result.page.nextCursor));
|
|
4272
4578
|
} catch (err) {
|
|
4273
|
-
|
|
4579
|
+
fail2(err, mode);
|
|
4274
4580
|
}
|
|
4275
4581
|
});
|
|
4276
4582
|
actions.command("get <action>").description("Describe one Topolo action by exact action id").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
@@ -4283,7 +4589,7 @@ function registerActionsCommand(program2) {
|
|
|
4283
4589
|
const entry = await topolo.client.getAction(action);
|
|
4284
4590
|
emit(entry, mode, () => formatActionDetail(entry));
|
|
4285
4591
|
} catch (err) {
|
|
4286
|
-
|
|
4592
|
+
fail2(err, mode);
|
|
4287
4593
|
}
|
|
4288
4594
|
});
|
|
4289
4595
|
actions.command("call <action>").description("Call a permission-scoped Topolo action").option("-d, --data <json-or-@file>", "JSON action input or @path to a JSON file", "{}").option("--resource-type <type>", "App-local resource alias, such as workspace").option("--resource-id <id>", "Resource id for the selected resource type").option("--confirm", "Acknowledge a mutating action").option("--detail", "Return the complete action response instead of a bounded agent projection").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
@@ -4302,7 +4608,7 @@ function registerActionsCommand(program2) {
|
|
|
4302
4608
|
const output = detailRequested(opts.detail, command) ? result : compactActionResult(action, result);
|
|
4303
4609
|
emit(output, mode, () => JSON.stringify(output, null, 2));
|
|
4304
4610
|
} catch (err) {
|
|
4305
|
-
|
|
4611
|
+
fail2(err, mode);
|
|
4306
4612
|
}
|
|
4307
4613
|
});
|
|
4308
4614
|
actions.command("upload <action>").description("Upload one local file through an action-declared upload contract").requiredOption("--file <path>", "Local file to upload").option("--content-type <type>", "MIME type; inferred for common file extensions").option("-d, --data <json-or-@file>", "Additional JSON action input or @path to a JSON file", "{}").option("--complete-data <json-or-@file>", "Additional input for a declared completion action", "{}").option("--resource-type <type>", "App-local resource alias, such as workspace").option("--resource-id <id>", "Resource id for the selected resource type").option("--confirm", "Acknowledge a mutating upload action").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
@@ -4326,7 +4632,7 @@ function registerActionsCommand(program2) {
|
|
|
4326
4632
|
);
|
|
4327
4633
|
emit(result, mode, () => JSON.stringify(result, null, 2));
|
|
4328
4634
|
} catch (err) {
|
|
4329
|
-
|
|
4635
|
+
fail2(err, mode);
|
|
4330
4636
|
}
|
|
4331
4637
|
});
|
|
4332
4638
|
actions.command("validate <action>").description("Validate action input locally against the published JSON Schema").option("-d, --data <json-or-@file>", "JSON action input or @path to a JSON file", "{}").option("--detail", "Include the full action contract").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
@@ -4337,7 +4643,7 @@ function registerActionsCommand(program2) {
|
|
|
4337
4643
|
const output = detailRequested(opts.detail, command) ? result : compactActionValidation(result.action.actionId, result.validation);
|
|
4338
4644
|
emit(output, mode, () => result.validation.valid ? `Valid input for ${result.action.actionId}.` : result.validation.errors.map((issue) => `${issue.path}: ${issue.message}`).join("\n"));
|
|
4339
4645
|
} catch (err) {
|
|
4340
|
-
|
|
4646
|
+
fail2(err, mode);
|
|
4341
4647
|
}
|
|
4342
4648
|
});
|
|
4343
4649
|
actions.command("prepare <action>").description("Prepare one action with identity, example, validation, exact request, verification, and rollback").option("-d, --data <json-or-@file>", "JSON action input or @path; defaults to one valid published example").option("--resource-type <type>", "App-local resource alias, such as workspace").option("--resource-id <id>", "Resource id for the selected resource type").option("--confirm", "Include confirmation in the plan without executing it").option("--json", "Output compact JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
@@ -4353,7 +4659,7 @@ function registerActionsCommand(program2) {
|
|
|
4353
4659
|
const output = compactActionPreparation(preparation, await actionExecutionContext(topolo));
|
|
4354
4660
|
emit(output, mode, () => formatActionPreparation(output));
|
|
4355
4661
|
} catch (err) {
|
|
4356
|
-
|
|
4662
|
+
fail2(err, mode);
|
|
4357
4663
|
}
|
|
4358
4664
|
});
|
|
4359
4665
|
actions.command("plan <action>").description("Validate and preview the exact request, confirmation, verification, and rollback plan").option("-d, --data <json-or-@file>", "JSON action input or @path to a JSON file", "{}").option("--resource-type <type>", "App-local resource alias, such as workspace").option("--resource-id <id>", "Resource id for the selected resource type").option("--confirm", "Include confirmation in the plan without executing it").option("--detail", "Include the full action contract").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
@@ -4367,7 +4673,7 @@ function registerActionsCommand(program2) {
|
|
|
4367
4673
|
});
|
|
4368
4674
|
emit(detailRequested(opts.detail, command) ? plan : compactActionPlan(plan), mode, () => formatActionPlan(plan));
|
|
4369
4675
|
} catch (err) {
|
|
4370
|
-
|
|
4676
|
+
fail2(err, mode);
|
|
4371
4677
|
}
|
|
4372
4678
|
});
|
|
4373
4679
|
actions.command("examples <action>").description("Show published examples and public docs for one action").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
@@ -4382,7 +4688,7 @@ function registerActionsCommand(program2) {
|
|
|
4382
4688
|
};
|
|
4383
4689
|
emit(result, mode, () => result.examples.length ? JSON.stringify(result, null, 2) : `No inline examples published. Docs: ${result.docsUrl || "not published"}`);
|
|
4384
4690
|
} catch (err) {
|
|
4385
|
-
|
|
4691
|
+
fail2(err, mode);
|
|
4386
4692
|
}
|
|
4387
4693
|
});
|
|
4388
4694
|
actions.command("capabilities").description("List every published action for one application (its capability contract)").option("-s, --service <service>", "App id, slug, or name").option("--summary", "Return compact capability counts without action rows").option("--detail", "Include full schemas and agent contracts").option("--json", "Output JSON").option("--no-json", "Output human-readable table").action(async (opts, command) => {
|
|
@@ -4417,7 +4723,7 @@ function registerActionsCommand(program2) {
|
|
|
4417
4723
|
const output = opts.summary ? summarizeActionCapabilities(resolvedService, discovered) : detailRequested(opts.detail, command) ? result : { actions: discovered.map(compactActionCatalogEntry), page: result.page };
|
|
4418
4724
|
emit(output, mode, () => opts.summary ? formatActionCapabilitySummary(summarizeActionCapabilities(resolvedService, discovered)) : formatActionRows(discovered));
|
|
4419
4725
|
} catch (err) {
|
|
4420
|
-
|
|
4726
|
+
fail2(err, mode);
|
|
4421
4727
|
}
|
|
4422
4728
|
});
|
|
4423
4729
|
}
|
|
@@ -4605,7 +4911,7 @@ function registerAppsCommand(program2) {
|
|
|
4605
4911
|
const applications = publicApplicationRows(result.services);
|
|
4606
4912
|
emit({ applications, page: result.page }, mode, () => formatPublicApplicationRows(applications));
|
|
4607
4913
|
} catch (error) {
|
|
4608
|
-
|
|
4914
|
+
fail2(error, mode);
|
|
4609
4915
|
}
|
|
4610
4916
|
});
|
|
4611
4917
|
apps.command("get <application>").description("Show one application available to the current Topolo credential").option("--json", "Output JSON", true).option("--no-json", "Output human-readable details").action(async (application, opts) => {
|
|
@@ -4617,7 +4923,7 @@ function registerAppsCommand(program2) {
|
|
|
4617
4923
|
if (!selected) throw new Error(`Application "${application}" is not a customer application.`);
|
|
4618
4924
|
emit({ application: selected }, mode, () => formatPublicApplication(selected));
|
|
4619
4925
|
} catch (error) {
|
|
4620
|
-
|
|
4926
|
+
fail2(error, mode);
|
|
4621
4927
|
}
|
|
4622
4928
|
});
|
|
4623
4929
|
}
|
|
@@ -4676,7 +4982,7 @@ function registerServicesCommand(program2) {
|
|
|
4676
4982
|
const rows = buildRows(result.services, config.serviceUrls, environment);
|
|
4677
4983
|
emit({ environment, services: rows, page: result.page }, mode, () => formatRows(rows, environment, result.page.nextCursor));
|
|
4678
4984
|
} catch (err) {
|
|
4679
|
-
|
|
4985
|
+
fail2(err, mode);
|
|
4680
4986
|
}
|
|
4681
4987
|
});
|
|
4682
4988
|
}
|
|
@@ -4769,7 +5075,7 @@ function registerOAuthCommands(program2) {
|
|
|
4769
5075
|
return clients2.map((client) => `${client.client_id} ${client.name} ${client.status}`).join("\n");
|
|
4770
5076
|
});
|
|
4771
5077
|
} catch (err) {
|
|
4772
|
-
|
|
5078
|
+
fail2(err, mode);
|
|
4773
5079
|
}
|
|
4774
5080
|
});
|
|
4775
5081
|
clients.command("create").description("Create a Login with Topolo OAuth client.").requiredOption("--name <name>", "Client display name").requiredOption("--developer-id <id>", "Topolo developer id that owns this client").option("--developer-app-id <id>", "Developer app id to associate with this client").option("--description <description>", "Client description").option("--redirect-uri <uri...>", "Allowed callback URI. Repeat or pass a space-separated list.").option("--scope <scope...>", "Allowed scope. Defaults to openid profile email.").option("--public", "Create a public PKCE client instead of a confidential client").option("--homepage-url <url>", "Client homepage URL").option("--logo-url <url>", "Client logo URL").option("--json", "Emit JSON output").action(async (options) => {
|
|
@@ -4789,7 +5095,7 @@ function registerOAuthCommands(program2) {
|
|
|
4789
5095
|
].join("\n");
|
|
4790
5096
|
});
|
|
4791
5097
|
} catch (err) {
|
|
4792
|
-
|
|
5098
|
+
fail2(err, mode);
|
|
4793
5099
|
}
|
|
4794
5100
|
});
|
|
4795
5101
|
clients.command("revoke").description("Revoke an OAuth client for all users.").argument("<id>", "OAuth client record id, for example oac_...").option("--json", "Emit JSON output").action(async (id, options) => {
|
|
@@ -4800,7 +5106,7 @@ function registerOAuthCommands(program2) {
|
|
|
4800
5106
|
});
|
|
4801
5107
|
emit(payload, mode, () => `Revoked OAuth client ${id}`);
|
|
4802
5108
|
} catch (err) {
|
|
4803
|
-
|
|
5109
|
+
fail2(err, mode);
|
|
4804
5110
|
}
|
|
4805
5111
|
});
|
|
4806
5112
|
clients.command("rotate-secret").description("Rotate a confidential OAuth client secret.").argument("<id>", "OAuth client record id, for example oac_...").option("--json", "Emit JSON output").action(async (id, options) => {
|
|
@@ -4811,7 +5117,7 @@ function registerOAuthCommands(program2) {
|
|
|
4811
5117
|
});
|
|
4812
5118
|
emit(payload, mode, () => `New client secret: ${payload.data?.client_secret ?? ""}`);
|
|
4813
5119
|
} catch (err) {
|
|
4814
|
-
|
|
5120
|
+
fail2(err, mode);
|
|
4815
5121
|
}
|
|
4816
5122
|
});
|
|
4817
5123
|
}
|
|
@@ -4856,8 +5162,8 @@ async function authFetchJson(path, init = {}) {
|
|
|
4856
5162
|
}
|
|
4857
5163
|
headers.set("Authorization", `Bearer ${credential.accessToken}`);
|
|
4858
5164
|
const response = await fetch(`${authBaseUrl}${path}`, { ...init, headers });
|
|
4859
|
-
const
|
|
4860
|
-
const payload =
|
|
5165
|
+
const text2 = await response.text();
|
|
5166
|
+
const payload = text2 ? JSON.parse(text2) : {};
|
|
4861
5167
|
if (!response.ok || payload.success === false) {
|
|
4862
5168
|
const err = new Error(payload.message || payload.error_description || payload.error || "Topolo Auth request failed");
|
|
4863
5169
|
err.code = payload.code || payload.error;
|
|
@@ -4883,7 +5189,7 @@ function registerIntegrationsCommand(program2) {
|
|
|
4883
5189
|
const prompt = buildLoginWithTopoloPrompt(framework);
|
|
4884
5190
|
emit({ integration, framework, prompt }, mode, () => prompt);
|
|
4885
5191
|
} catch (err) {
|
|
4886
|
-
|
|
5192
|
+
fail2(err, mode);
|
|
4887
5193
|
}
|
|
4888
5194
|
});
|
|
4889
5195
|
integrations.command("check").description("Check whether a repo has the Login with Topolo integration pieces.").argument("<integration>", "Integration check id, currently login-with-topolo").option("--repo-root <path>", "Repository root to inspect", ".").option("--json", "Emit JSON output").action(async (integration, options) => {
|
|
@@ -4894,7 +5200,7 @@ function registerIntegrationsCommand(program2) {
|
|
|
4894
5200
|
emit(result, mode, () => formatCheckResult(result));
|
|
4895
5201
|
if (!result.passed) process.exitCode = 1;
|
|
4896
5202
|
} catch (err) {
|
|
4897
|
-
|
|
5203
|
+
fail2(err, mode);
|
|
4898
5204
|
}
|
|
4899
5205
|
});
|
|
4900
5206
|
}
|
|
@@ -5320,9 +5626,9 @@ async function readTomlRegistrations(path, agent) {
|
|
|
5320
5626
|
return [...registrations.values()].filter((registration) => registration.command);
|
|
5321
5627
|
}
|
|
5322
5628
|
async function readGooseRegistrations(path) {
|
|
5323
|
-
const
|
|
5629
|
+
const text2 = await readFile3(path, "utf8");
|
|
5324
5630
|
const registrations = [];
|
|
5325
|
-
const lines =
|
|
5631
|
+
const lines = text2.split(/\r?\n/);
|
|
5326
5632
|
let inExtensions = false;
|
|
5327
5633
|
let inTopolo = false;
|
|
5328
5634
|
let current;
|
|
@@ -5543,8 +5849,8 @@ async function unregisterAgentConfig(agent, homeDir, name, projectDir) {
|
|
|
5543
5849
|
if (config.format === "json") {
|
|
5544
5850
|
await unregisterJson(config, agent, name);
|
|
5545
5851
|
} else if (config.format === "toml") {
|
|
5546
|
-
const
|
|
5547
|
-
await atomicWrite(config.path,
|
|
5852
|
+
const text2 = withoutMcpServerSections(await readFile4(config.path, "utf8"), name);
|
|
5853
|
+
await atomicWrite(config.path, text2 ? text2 + "\n" : "");
|
|
5548
5854
|
} else {
|
|
5549
5855
|
await unregisterGoose(config.path, name);
|
|
5550
5856
|
}
|
|
@@ -5604,12 +5910,12 @@ async function unregisterJson(config, agent, name) {
|
|
|
5604
5910
|
}
|
|
5605
5911
|
async function registerToml(path, agent, mcp, registration) {
|
|
5606
5912
|
const existing = existsSync3(path) ? await readFile4(path, "utf8") : "";
|
|
5607
|
-
const
|
|
5913
|
+
const text2 = withoutMcpServerSections(existing, registration.name);
|
|
5608
5914
|
const args = mcp.args.length ? "\nargs = [" + mcp.args.map((item) => JSON.stringify(item)).join(", ") + "]" : "";
|
|
5609
5915
|
const environment = registrationEnvironment(agent, registration);
|
|
5610
5916
|
const environmentText = Object.entries(environment).map(([key, value]) => JSON.stringify(key) + " = " + JSON.stringify(value)).join(", ");
|
|
5611
5917
|
const block = agent === "grok" ? "\n\n[mcp_servers." + registration.name + "]\ncommand = " + JSON.stringify(mcp.command) + args + "\nenv = { " + environmentText + " }\n" : "\n\n[mcp_servers." + registration.name + "]\ncommand = " + JSON.stringify(mcp.command) + args + "\n\n[mcp_servers." + registration.name + ".env]\n" + Object.entries(environment).map(([key, value]) => key + " = " + JSON.stringify(value)).join("\n") + "\n";
|
|
5612
|
-
await atomicWrite(path,
|
|
5918
|
+
await atomicWrite(path, text2 + block);
|
|
5613
5919
|
}
|
|
5614
5920
|
async function registerGoose(path, mcp, registration) {
|
|
5615
5921
|
const existing = existsSync3(path) ? await readFile4(path, "utf8") : "";
|
|
@@ -5632,8 +5938,8 @@ async function unregisterGoose(path, name) {
|
|
|
5632
5938
|
const existing = await readFile4(path, "utf8");
|
|
5633
5939
|
await atomicWrite(path, removeGooseExtension(existing, name));
|
|
5634
5940
|
}
|
|
5635
|
-
function removeGooseExtension(
|
|
5636
|
-
const lines =
|
|
5941
|
+
function removeGooseExtension(text2, name) {
|
|
5942
|
+
const lines = text2.split(/\r?\n/);
|
|
5637
5943
|
const kept = [];
|
|
5638
5944
|
let inExtensions = false;
|
|
5639
5945
|
let skipping = false;
|
|
@@ -5653,11 +5959,11 @@ function removeGooseExtension(text, name) {
|
|
|
5653
5959
|
}
|
|
5654
5960
|
return kept.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
5655
5961
|
}
|
|
5656
|
-
function insertGooseExtension(
|
|
5657
|
-
if (!
|
|
5658
|
-
const lines =
|
|
5962
|
+
function insertGooseExtension(text2, block) {
|
|
5963
|
+
if (!text2.trim()) return "extensions:\n" + block + "\n";
|
|
5964
|
+
const lines = text2.split(/\r?\n/);
|
|
5659
5965
|
const extensionIndex = lines.findIndex((line) => /^extensions:\s*$/.test(line));
|
|
5660
|
-
if (extensionIndex < 0) return
|
|
5966
|
+
if (extensionIndex < 0) return text2.trimEnd() + "\n\nextensions:\n" + block + "\n";
|
|
5661
5967
|
let end = lines.length;
|
|
5662
5968
|
for (let index = extensionIndex + 1; index < lines.length; index += 1) {
|
|
5663
5969
|
if (/^\S/.test(lines[index]) && lines[index].trim()) {
|
|
@@ -5685,11 +5991,11 @@ async function atomicWrite(path, contents) {
|
|
|
5685
5991
|
await writeFile2(temporaryPath, contents, { encoding: "utf8", mode: 384 });
|
|
5686
5992
|
await rename(temporaryPath, path);
|
|
5687
5993
|
}
|
|
5688
|
-
function withoutMcpServerSections(
|
|
5994
|
+
function withoutMcpServerSections(text2, name) {
|
|
5689
5995
|
const section = "[mcp_servers." + name + "]";
|
|
5690
5996
|
const environmentSection = "[mcp_servers." + name + ".env]";
|
|
5691
5997
|
let skipping = false;
|
|
5692
|
-
return
|
|
5998
|
+
return text2.split(/\r?\n/).filter((line) => {
|
|
5693
5999
|
const trimmed = line.trim();
|
|
5694
6000
|
if (trimmed === section || trimmed === environmentSection) {
|
|
5695
6001
|
skipping = true;
|
|
@@ -5800,7 +6106,7 @@ function registerSkillsCommand(program2) {
|
|
|
5800
6106
|
assertAgentSetupSucceeded(results);
|
|
5801
6107
|
emit({ installed: results, mcpCommand: mcp }, mode, () => formatInstall(results, mcp));
|
|
5802
6108
|
} catch (err) {
|
|
5803
|
-
|
|
6109
|
+
fail2(err, mode);
|
|
5804
6110
|
}
|
|
5805
6111
|
}
|
|
5806
6112
|
);
|
|
@@ -5816,7 +6122,7 @@ function registerSkillsCommand(program2) {
|
|
|
5816
6122
|
});
|
|
5817
6123
|
emit({ agents: rows }, mode, () => formatStatus(rows));
|
|
5818
6124
|
} catch (err) {
|
|
5819
|
-
|
|
6125
|
+
fail2(err, mode);
|
|
5820
6126
|
}
|
|
5821
6127
|
});
|
|
5822
6128
|
skills.command("uninstall").description("Remove the skill file and MCP registration for each agent").option("--claude", "Target Claude Code only").option("--codex", "Target Codex only").option("--agent <host...>", "Target one or more hosts by id").option("--all", "Remove from every supported host").option("--project <dir>", "Remove project-scoped host files under <dir>").option("--agent-home <dir>", "Target an alternate agent home (managed installs and clean-room verification)").option("--name <name>", "Remove one registration (defaults to topolo)").option("--json", "Output JSON", true).option("--no-json", "Output human-readable text").action(async (opts) => {
|
|
@@ -5832,7 +6138,7 @@ function registerSkillsCommand(program2) {
|
|
|
5832
6138
|
() => removed.map((r) => `${AGENT_LABELS2[r.agent]}: ${r.name} removed=${r.registrationRemoved}, skill removed=${r.skillRemoved}`).join("\n")
|
|
5833
6139
|
);
|
|
5834
6140
|
} catch (err) {
|
|
5835
|
-
|
|
6141
|
+
fail2(err, mode);
|
|
5836
6142
|
}
|
|
5837
6143
|
});
|
|
5838
6144
|
}
|
|
@@ -5863,7 +6169,7 @@ function registerSetupCommand(program2) {
|
|
|
5863
6169
|
assertAgentSetupSucceeded(results);
|
|
5864
6170
|
emit({ installed: results, mcpCommand: mcp }, mode, () => formatInstall(results, mcp));
|
|
5865
6171
|
} catch (error) {
|
|
5866
|
-
|
|
6172
|
+
fail2(error, mode);
|
|
5867
6173
|
}
|
|
5868
6174
|
});
|
|
5869
6175
|
}
|
|
@@ -6051,7 +6357,7 @@ function registerDoctorCommand(program2) {
|
|
|
6051
6357
|
if (!result.ok) throw new Error(formatDoctor(result));
|
|
6052
6358
|
emit(result, mode, () => formatDoctor(result));
|
|
6053
6359
|
} catch (error) {
|
|
6054
|
-
|
|
6360
|
+
fail2(error, mode);
|
|
6055
6361
|
}
|
|
6056
6362
|
});
|
|
6057
6363
|
}
|
|
@@ -6242,7 +6548,7 @@ function registerContextCommand(program2) {
|
|
|
6242
6548
|
return lines.join("\n");
|
|
6243
6549
|
});
|
|
6244
6550
|
} catch (err) {
|
|
6245
|
-
|
|
6551
|
+
fail2(err, mode);
|
|
6246
6552
|
}
|
|
6247
6553
|
});
|
|
6248
6554
|
context.command("use <target>").description('Switch the active context (an organization name/slug, or "personal")').option("--json", "Output JSON", true).option("--no-json", "Output human-readable text").action(async (target, opts) => {
|
|
@@ -6345,7 +6651,7 @@ function registerContextCommand(program2) {
|
|
|
6345
6651
|
\u2713 Now acting as ${labelFor(resolved)} (${resolved.contextType}).`
|
|
6346
6652
|
);
|
|
6347
6653
|
} catch (err) {
|
|
6348
|
-
|
|
6654
|
+
fail2(err, mode);
|
|
6349
6655
|
}
|
|
6350
6656
|
});
|
|
6351
6657
|
}
|
|
@@ -6362,7 +6668,7 @@ function registerResourcesCommand(program2) {
|
|
|
6362
6668
|
const result = await topolo.client.listBindableResources(selector);
|
|
6363
6669
|
emit(result, mode, () => formatResources(result));
|
|
6364
6670
|
} catch (err) {
|
|
6365
|
-
|
|
6671
|
+
fail2(err, mode);
|
|
6366
6672
|
}
|
|
6367
6673
|
});
|
|
6368
6674
|
}
|
|
@@ -6390,7 +6696,7 @@ function registerWorkspacesCommand(program2) {
|
|
|
6390
6696
|
const result = await topolo.workspaces.list(application);
|
|
6391
6697
|
emit(result, mode, () => formatWorkspaceList(result));
|
|
6392
6698
|
} catch (error) {
|
|
6393
|
-
|
|
6699
|
+
fail2(error, mode);
|
|
6394
6700
|
}
|
|
6395
6701
|
});
|
|
6396
6702
|
workspaces.command("create <application>").description("Create a workspace when the target application policy permits it").requiredOption("--name <name>", "Workspace display name").option("--default", "Make the new workspace the application default").option("--confirm", "Acknowledge workspace creation").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (application, opts) => {
|
|
@@ -6404,7 +6710,7 @@ function registerWorkspacesCommand(program2) {
|
|
|
6404
6710
|
});
|
|
6405
6711
|
emit(result, mode, () => formatWorkspaceMutation(result));
|
|
6406
6712
|
} catch (error) {
|
|
6407
|
-
|
|
6713
|
+
fail2(error, mode);
|
|
6408
6714
|
}
|
|
6409
6715
|
});
|
|
6410
6716
|
workspaces.command("rename <application> <workspace-id>").description("Rename a caller-authorized workspace without changing its stable id").requiredOption("--name <name>", "New workspace display name").option("--confirm", "Acknowledge workspace rename").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (application, workspaceId, opts) => {
|
|
@@ -6414,7 +6720,7 @@ function registerWorkspacesCommand(program2) {
|
|
|
6414
6720
|
const result = await topolo.workspaces.rename(application, workspaceId, opts.name.trim(), opts.confirm === true);
|
|
6415
6721
|
emit(result, mode, () => formatWorkspaceMutation(result));
|
|
6416
6722
|
} catch (error) {
|
|
6417
|
-
|
|
6723
|
+
fail2(error, mode);
|
|
6418
6724
|
}
|
|
6419
6725
|
});
|
|
6420
6726
|
workspaces.command("default <application> <workspace-id>").description("Set the default workspace for one application").option("--confirm", "Acknowledge the default-workspace change").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (application, workspaceId, opts) => {
|
|
@@ -6424,7 +6730,7 @@ function registerWorkspacesCommand(program2) {
|
|
|
6424
6730
|
const result = await topolo.workspaces.setDefault(application, workspaceId, opts.confirm === true);
|
|
6425
6731
|
emit(result, mode, () => formatWorkspaceMutation(result));
|
|
6426
6732
|
} catch (error) {
|
|
6427
|
-
|
|
6733
|
+
fail2(error, mode);
|
|
6428
6734
|
}
|
|
6429
6735
|
});
|
|
6430
6736
|
}
|
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.
|
|
5
|
-
"topoloPublishedVersion": "0.
|
|
4
|
+
"version": "0.11.0",
|
|
5
|
+
"topoloPublishedVersion": "0.11.0",
|
|
6
6
|
"description": "Public command-line interface for Topolo customer organizations, developers, and agents.",
|
|
7
7
|
"license": "UNLICENSED",
|
|
8
8
|
"author": "Topolo",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
31
|
-
"@topolo/mcp": "0.
|
|
32
|
-
"@topolo/sdk": "0.
|
|
31
|
+
"@topolo/mcp": "0.12.0",
|
|
32
|
+
"@topolo/sdk": "0.17.1",
|
|
33
33
|
"commander": "^12.1.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|