@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.
Files changed (2) hide show
  1. package/dist/index.js +391 -85
  2. 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.14.4/node_modules/@topolo/sdk/dist/index.js
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 record = body;
177
- const topLevel = canonicalTopoloErrorCode(record.code);
176
+ const record2 = body;
177
+ const topLevel = canonicalTopoloErrorCode(record2.code);
178
178
  if (topLevel) return topLevel;
179
- const nested = record.error;
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 record = body;
195
- let candidate = record.details;
196
- const nested = record.error;
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 = record;
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 record = issue;
254
- const path = canonicalTopoloValidationPath(record.field) ?? canonicalTopoloValidationPath(record.path);
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 requestId = generateRequestId();
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 record = raw;
1460
- if (record.algorithm !== "sha256-v1" || typeof record.digest !== "string" || !/^[a-f0-9]{64}$/.test(record.digest) || !Number.isSafeInteger(record.actionCount) || Number(record.actionCount) < 0) {
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: record.digest,
1466
- actionCount: Number(record.actionCount)
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 record = nested;
2100
- if (Array.isArray(record.required)) return canonicalTopoloRequired(record.required);
2101
- if (record.details && typeof record.details === "object" && !Array.isArray(record.details)) {
2102
- const required = record.details.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 record = value;
2178
- const allKeys = Object.keys(record);
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(record[key], depth + 1, budget)]));
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.14.4/node_modules/@topolo/sdk/dist/node/credential-store.js
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 record = body;
3075
- const topLevel = canonicalTopoloErrorCode2(record.code);
3379
+ const record2 = body;
3380
+ const topLevel = canonicalTopoloErrorCode2(record2.code);
3076
3381
  if (topLevel) return topLevel;
3077
- const nested = record.error;
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 record = body;
3093
- let candidate = record.details;
3094
- const nested = record.error;
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 = record;
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 record = issue;
3152
- const path = canonicalTopoloValidationPath2(record.field) ?? canonicalTopoloValidationPath2(record.path);
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 fail(err, mode) {
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(error, mode);
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
- fail(error, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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 text = await response.text();
4860
- const payload = text ? JSON.parse(text) : {};
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
- fail(err, mode);
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
- fail(err, mode);
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 text = await readFile3(path, "utf8");
5629
+ const text2 = await readFile3(path, "utf8");
5324
5630
  const registrations = [];
5325
- const lines = text.split(/\r?\n/);
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 text = withoutMcpServerSections(await readFile4(config.path, "utf8"), name);
5547
- await atomicWrite(config.path, text ? text + "\n" : "");
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 text = withoutMcpServerSections(existing, registration.name);
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, text + block);
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(text, name) {
5636
- const lines = text.split(/\r?\n/);
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(text, block) {
5657
- if (!text.trim()) return "extensions:\n" + block + "\n";
5658
- const lines = text.split(/\r?\n/);
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 text.trimEnd() + "\n\nextensions:\n" + block + "\n";
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(text, name) {
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 text.split(/\r?\n/).filter((line) => {
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(error, mode);
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
- fail(error, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(err, mode);
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
- fail(error, mode);
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
- fail(error, mode);
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
- fail(error, mode);
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
- fail(error, mode);
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.10.1",
5
- "topoloPublishedVersion": "0.10.1",
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.11.12",
32
- "@topolo/sdk": "0.14.4",
31
+ "@topolo/mcp": "0.12.0",
32
+ "@topolo/sdk": "0.17.1",
33
33
  "commander": "^12.1.0"
34
34
  },
35
35
  "devDependencies": {