@topolo/sdk 0.21.2 → 0.21.4

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.
@@ -29,6 +29,7 @@ __export(credential_store_exports, {
29
29
  configPath: () => configPath,
30
30
  contextKey: () => contextKey,
31
31
  createPlatformInstallationSecretBackend: () => createPlatformInstallationSecretBackend,
32
+ createProtectedActionSecretStore: () => createProtectedActionSecretStore,
32
33
  credentialSourceFromConfig: () => credentialSourceFromConfig,
33
34
  deleteExternalAgentInstallation: () => deleteExternalAgentInstallation,
34
35
  envVarPrefix: () => envVarPrefix,
@@ -55,7 +56,7 @@ module.exports = __toCommonJS(credential_store_exports);
55
56
  var import_promises2 = require("fs/promises");
56
57
  var import_node_fs2 = require("fs");
57
58
  var import_node_os2 = require("os");
58
- var import_node_path2 = require("path");
59
+ var import_node_path3 = require("path");
59
60
 
60
61
  // src/errors.ts
61
62
  var TopoloSdkError = class extends Error {
@@ -411,68 +412,12 @@ var TopoloOAuth = class {
411
412
  }
412
413
  };
413
414
 
414
- // src/environment.ts
415
- var TOPOLO_RUNTIME_ENVIRONMENTS = ["production", "staging", "development", "local"];
416
- var TOPOLO_ENVIRONMENT_SERVICE_URLS = {
417
- production: {
418
- auth: "https://auth.topolo.app",
419
- developers: "https://developers.topolo.app"
420
- },
421
- staging: {
422
- auth: "https://auth.stg.topolo.us",
423
- developers: "https://developers.stg.topolo.us"
424
- },
425
- development: {
426
- auth: "https://auth.topolo.dev",
427
- developers: "https://developers.topolo.dev"
428
- },
429
- local: {}
430
- };
431
- function hostnameHasSuffix(hostname, suffix) {
432
- return hostname === suffix || hostname.endsWith(`.${suffix}`);
433
- }
434
- function workersDevAccountMatches(hostname, account) {
435
- if (!hostname.endsWith(".workers.dev")) return false;
436
- const labels = hostname.slice(0, -".workers.dev".length).split(".");
437
- return labels.length >= 2 && labels.at(-1) === account;
438
- }
439
- function originMatchesEnvironment(url, environment) {
440
- const hostname = url.hostname.toLowerCase();
441
- if (environment === "local") {
442
- return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "[::1]";
443
- }
444
- if (url.protocol !== "https:") return false;
445
- if (environment === "production") {
446
- return hostnameHasSuffix(hostname, "topolo.app") || hostnameHasSuffix(hostname, "topolo.io") || hostname === "api.ol0.me" || workersDevAccountMatches(hostname, "topolo");
447
- }
448
- if (environment === "staging") {
449
- return hostnameHasSuffix(hostname, "stg.topolo.us") || workersDevAccountMatches(hostname, "topolo-staging");
450
- }
451
- return hostnameHasSuffix(hostname, "topolo.dev") || workersDevAccountMatches(hostname, "topolo-development");
452
- }
453
- function assertTopoloServiceUrlEnvironment(service, value, environment) {
454
- let url;
455
- try {
456
- url = new URL(value);
457
- } catch {
458
- throw new TopoloSdkError("invalid_service_url", `Invalid URL configured for Topolo service "${service}".`);
459
- }
460
- if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !originMatchesEnvironment(url, environment)) {
461
- throw new TopoloSdkError(
462
- "environment_mismatch",
463
- `Refusing Topolo service "${service}": ${url.origin} does not belong to ${environment}.`
464
- );
465
- }
466
- return url.origin;
467
- }
468
- function topoloServiceOrigins(serviceUrls, environment) {
469
- return Object.fromEntries(
470
- Object.entries(serviceUrls ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(([service, value]) => [
471
- service,
472
- assertTopoloServiceUrlEnvironment(service, value, environment)
473
- ])
474
- );
475
- }
415
+ // src/node/action-secret-store.ts
416
+ var import_node_crypto = require("crypto");
417
+ var import_node_path2 = require("path");
418
+
419
+ // src/action-secrets.ts
420
+ var ACTION_SECRET_PREFIX = "topolo-secret:";
476
421
 
477
422
  // src/node/installation-store.ts
478
423
  var import_node_child_process = require("child_process");
@@ -522,6 +467,7 @@ function createPlatformInstallationSecretBackend(options = {}) {
522
467
  const platform2 = options.platform ?? (0, import_node_os.platform)();
523
468
  const spawn = options.spawn ?? ((command, args, spawnOptions) => (0, import_node_child_process.spawnSync)(command, args, spawnOptions));
524
469
  const root = options.root ?? installationStorePath();
470
+ const service = options.service ?? KEYCHAIN_SERVICE;
525
471
  if (platform2 === "darwin") {
526
472
  const write = (account, value) => runOrThrow(spawn("/usr/bin/expect", ["-c", MACOS_KEYCHAIN_WRITE_SCRIPT], {
527
473
  input: `${value}
@@ -530,7 +476,7 @@ function createPlatformInstallationSecretBackend(options = {}) {
530
476
  env: {
531
477
  ...process.env,
532
478
  TOPOLO_KEYCHAIN_ACCOUNT: account,
533
- TOPOLO_KEYCHAIN_SERVICE: KEYCHAIN_SERVICE
479
+ TOPOLO_KEYCHAIN_SERVICE: service
534
480
  }
535
481
  }), "macOS Keychain write");
536
482
  const read = (account) => {
@@ -539,7 +485,7 @@ function createPlatformInstallationSecretBackend(options = {}) {
539
485
  "-a",
540
486
  account,
541
487
  "-s",
542
- KEYCHAIN_SERVICE,
488
+ service,
543
489
  "-w"
544
490
  ], { encoding: "utf8" });
545
491
  if (result.status === 44) return null;
@@ -551,7 +497,7 @@ function createPlatformInstallationSecretBackend(options = {}) {
551
497
  "-a",
552
498
  account,
553
499
  "-s",
554
- KEYCHAIN_SERVICE
500
+ service
555
501
  ], { encoding: "utf8" });
556
502
  if (result.status !== 44) runOrThrow(result, "macOS Keychain delete");
557
503
  };
@@ -598,9 +544,9 @@ function createPlatformInstallationSecretBackend(options = {}) {
598
544
  async set(account, value) {
599
545
  runOrThrow(spawn("secret-tool", [
600
546
  "store",
601
- "--label=Topolo external agent installation",
547
+ "--label=Topolo protected credential",
602
548
  "service",
603
- KEYCHAIN_SERVICE,
549
+ service,
604
550
  "account",
605
551
  account
606
552
  ], { input: value, encoding: "utf8" }), "Linux Secret Service write");
@@ -609,7 +555,7 @@ function createPlatformInstallationSecretBackend(options = {}) {
609
555
  const result = spawn("secret-tool", [
610
556
  "lookup",
611
557
  "service",
612
- KEYCHAIN_SERVICE,
558
+ service,
613
559
  "account",
614
560
  account
615
561
  ], { encoding: "utf8" });
@@ -620,7 +566,7 @@ function createPlatformInstallationSecretBackend(options = {}) {
620
566
  const result = spawn("secret-tool", [
621
567
  "clear",
622
568
  "service",
623
- KEYCHAIN_SERVICE,
569
+ service,
624
570
  "account",
625
571
  account
626
572
  ], { encoding: "utf8" });
@@ -691,6 +637,118 @@ async function deleteExternalAgentInstallation(environment, id, backend = create
691
637
  await (0, import_promises.rm)(installationPath(environment, id), { force: true });
692
638
  }
693
639
 
640
+ // src/node/action-secret-store.ts
641
+ function createProtectedActionSecretStore(options) {
642
+ const backend = options.backend ?? createPlatformInstallationSecretBackend({
643
+ service: "topolo-action-handoff",
644
+ root: (0, import_node_path2.join)(installationStorePath(), "action-handoff")
645
+ });
646
+ const now = options.now ?? Date.now;
647
+ async function account(reference) {
648
+ if (!/^topolo-secret:[0-9a-f-]{36}$/.test(reference)) throw new TopoloSdkError("invalid_action_secret_reference", "Invalid protected action reference.");
649
+ const identity = await options.identity();
650
+ if (!identity.user.id) throw new TopoloSdkError("action_secret_identity_required", "A verified identity is required for protected action handoff.");
651
+ if (identity.kind === "api_key" && !identity.apiKey?.id) throw new TopoloSdkError("action_secret_identity_required", "A verified API key identity is required for protected action handoff.");
652
+ const scope = (0, import_node_crypto.createHash)("sha256").update(JSON.stringify([
653
+ options.environment,
654
+ identity.user.principalId ?? identity.user.id,
655
+ identity.organization?.id ?? null,
656
+ identity.apiKey?.id ?? null
657
+ ])).digest("hex");
658
+ return `${scope}-${reference.slice(ACTION_SECRET_PREFIX.length)}`;
659
+ }
660
+ return {
661
+ async save(value) {
662
+ const reference = `${ACTION_SECRET_PREFIX}${(0, import_node_crypto.randomUUID)()}`;
663
+ await backend.set(await account(reference), JSON.stringify({ value, expiresAt: now() + 30 * 60 * 1e3 }));
664
+ return reference;
665
+ },
666
+ async load(reference) {
667
+ const key = await account(reference);
668
+ const encoded = await backend.get(key);
669
+ if (!encoded) throw new TopoloSdkError("action_secret_unavailable", "Protected action reference is missing or belongs to another identity or environment.");
670
+ let record;
671
+ try {
672
+ record = JSON.parse(encoded);
673
+ } catch {
674
+ throw new TopoloSdkError("action_secret_invalid", "Protected action reference is corrupt.");
675
+ }
676
+ if (!Number.isFinite(record?.expiresAt) || !Object.hasOwn(record, "value")) throw new TopoloSdkError("action_secret_invalid", "Protected action reference is corrupt.");
677
+ if (record.expiresAt <= now()) {
678
+ await backend.delete(key);
679
+ throw new TopoloSdkError("action_secret_expired", "Protected action reference expired. Repeat the originating action if its contract permits.");
680
+ }
681
+ return record.value;
682
+ },
683
+ async delete(reference) {
684
+ await backend.delete(await account(reference));
685
+ }
686
+ };
687
+ }
688
+
689
+ // src/environment.ts
690
+ var TOPOLO_RUNTIME_ENVIRONMENTS = ["production", "staging", "development", "local"];
691
+ var TOPOLO_ENVIRONMENT_SERVICE_URLS = {
692
+ production: {
693
+ auth: "https://auth.topolo.app",
694
+ developers: "https://developers.topolo.app"
695
+ },
696
+ staging: {
697
+ auth: "https://auth.stg.topolo.us",
698
+ developers: "https://developers.stg.topolo.us"
699
+ },
700
+ development: {
701
+ auth: "https://auth.topolo.dev",
702
+ developers: "https://developers.topolo.dev"
703
+ },
704
+ local: {}
705
+ };
706
+ function hostnameHasSuffix(hostname, suffix) {
707
+ return hostname === suffix || hostname.endsWith(`.${suffix}`);
708
+ }
709
+ function workersDevAccountMatches(hostname, account) {
710
+ if (!hostname.endsWith(".workers.dev")) return false;
711
+ const labels = hostname.slice(0, -".workers.dev".length).split(".");
712
+ return labels.length >= 2 && labels.at(-1) === account;
713
+ }
714
+ function originMatchesEnvironment(url, environment) {
715
+ const hostname = url.hostname.toLowerCase();
716
+ if (environment === "local") {
717
+ return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "[::1]";
718
+ }
719
+ if (url.protocol !== "https:") return false;
720
+ if (environment === "production") {
721
+ return hostnameHasSuffix(hostname, "topolo.app") || hostnameHasSuffix(hostname, "topolo.io") || hostname === "api.ol0.me" || workersDevAccountMatches(hostname, "topolo");
722
+ }
723
+ if (environment === "staging") {
724
+ return hostnameHasSuffix(hostname, "stg.topolo.us") || workersDevAccountMatches(hostname, "topolo-staging");
725
+ }
726
+ return hostnameHasSuffix(hostname, "topolo.dev") || workersDevAccountMatches(hostname, "topolo-development");
727
+ }
728
+ function assertTopoloServiceUrlEnvironment(service, value, environment) {
729
+ let url;
730
+ try {
731
+ url = new URL(value);
732
+ } catch {
733
+ throw new TopoloSdkError("invalid_service_url", `Invalid URL configured for Topolo service "${service}".`);
734
+ }
735
+ if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !originMatchesEnvironment(url, environment)) {
736
+ throw new TopoloSdkError(
737
+ "environment_mismatch",
738
+ `Refusing Topolo service "${service}": ${url.origin} does not belong to ${environment}.`
739
+ );
740
+ }
741
+ return url.origin;
742
+ }
743
+ function topoloServiceOrigins(serviceUrls, environment) {
744
+ return Object.fromEntries(
745
+ Object.entries(serviceUrls ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(([service, value]) => [
746
+ service,
747
+ assertTopoloServiceUrlEnvironment(service, value, environment)
748
+ ])
749
+ );
750
+ }
751
+
694
752
  // src/node/credential-store.ts
695
753
  var TOPOLO_ENVIRONMENTS = TOPOLO_RUNTIME_ENVIRONMENTS;
696
754
  function contextKey(contextType, organizationId) {
@@ -700,8 +758,8 @@ var ENVIRONMENT_DEFAULT_SERVICE_URLS = TOPOLO_ENVIRONMENT_SERVICE_URLS;
700
758
  function configPath() {
701
759
  const override = process.env.TOPOLO_CONFIG_PATH;
702
760
  if (override) return override;
703
- const base = process.env.XDG_CONFIG_HOME || ((0, import_node_os2.platform)() === "win32" ? (0, import_node_path2.join)((0, import_node_os2.homedir)(), "AppData", "Roaming") : (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".config"));
704
- return (0, import_node_path2.join)(base, "topolo", "config.json");
761
+ const base = process.env.XDG_CONFIG_HOME || ((0, import_node_os2.platform)() === "win32" ? (0, import_node_path3.join)((0, import_node_os2.homedir)(), "AppData", "Roaming") : (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".config"));
762
+ return (0, import_node_path3.join)(base, "topolo", "config.json");
705
763
  }
706
764
  async function readConfig() {
707
765
  const path = configPath();
@@ -715,7 +773,7 @@ async function readConfig() {
715
773
  }
716
774
  async function writeConfig(config) {
717
775
  const path = configPath();
718
- await (0, import_promises2.mkdir)((0, import_node_path2.dirname)(path), { recursive: true });
776
+ await (0, import_promises2.mkdir)((0, import_node_path3.dirname)(path), { recursive: true });
719
777
  await (0, import_promises2.writeFile)(path, JSON.stringify(config, null, 2) + "\n", "utf8");
720
778
  if ((0, import_node_os2.platform)() !== "win32") {
721
779
  try {
@@ -835,7 +893,7 @@ var CONFIG_LOCK_TIMEOUT_MS = 5e3;
835
893
  var CONFIG_LOCK_STALE_MS = 12e4;
836
894
  async function withConfigWriteLock(operation) {
837
895
  const lockPath = `${configPath()}.lock`;
838
- await (0, import_promises2.mkdir)((0, import_node_path2.dirname)(lockPath), { recursive: true });
896
+ await (0, import_promises2.mkdir)((0, import_node_path3.dirname)(lockPath), { recursive: true });
839
897
  const startedAt = Date.now();
840
898
  while (true) {
841
899
  try {
@@ -1012,6 +1070,7 @@ function resolvedProfileConfig(config, environment = selectedEnvironment()) {
1012
1070
  configPath,
1013
1071
  contextKey,
1014
1072
  createPlatformInstallationSecretBackend,
1073
+ createProtectedActionSecretStore,
1015
1074
  credentialSourceFromConfig,
1016
1075
  deleteExternalAgentInstallation,
1017
1076
  envVarPrefix,
@@ -26,11 +26,53 @@ type TopoloCredential = {
26
26
  expiresAt?: number;
27
27
  };
28
28
 
29
+ interface TopoloActionSecretStore {
30
+ save(value: unknown): Promise<string>;
31
+ load(reference: string): Promise<unknown>;
32
+ delete(reference: string): Promise<void>;
33
+ }
34
+
29
35
  declare const TOPOLO_RUNTIME_ENVIRONMENTS: readonly ["production", "staging", "development", "local"];
30
36
  type TopoloRuntimeEnvironment = (typeof TOPOLO_RUNTIME_ENVIRONMENTS)[number];
31
37
  declare function assertTopoloServiceUrlEnvironment(service: string, value: string, environment: TopoloRuntimeEnvironment): string;
32
38
  declare function topoloServiceOrigins(serviceUrls: Record<string, string> | undefined, environment: TopoloRuntimeEnvironment): Record<string, string>;
33
39
 
40
+ interface CredentialIntrospection {
41
+ kind: 'api_key' | 'access_token';
42
+ authorizationMode: 'context_rbac' | 'oauth_scopes' | 'session_rbac' | 'api_key' | 'unknown';
43
+ user: {
44
+ id: string;
45
+ email: string;
46
+ name: string | null;
47
+ role: string;
48
+ permissions: string[];
49
+ principalId?: string | null;
50
+ principalType?: 'agent_employee' | 'external_agent' | null;
51
+ accountableUserId?: string | null;
52
+ agentInstanceId?: string | null;
53
+ installationId?: string | null;
54
+ provider?: string | null;
55
+ model?: string | null;
56
+ serviceScopes?: string[];
57
+ policyRef?: string | null;
58
+ };
59
+ organization: {
60
+ id: string;
61
+ slug: string;
62
+ name: string;
63
+ } | null;
64
+ apiKey: {
65
+ id: string | null;
66
+ name: string | null;
67
+ appId: string | null;
68
+ scopes: string[];
69
+ resourceBindings: Array<{
70
+ resourceType: string;
71
+ resourceId: string;
72
+ }>;
73
+ } | null;
74
+ }
75
+
34
76
  interface ExternalAgentInstallation {
35
77
  id: string;
36
78
  environment: TopoloRuntimeEnvironment;
@@ -64,6 +106,7 @@ declare function createPlatformInstallationSecretBackend(options?: {
64
106
  platform?: NodeJS.Platform;
65
107
  spawn?: Spawn;
66
108
  root?: string;
109
+ service?: string;
67
110
  }): InstallationSecretBackend;
68
111
  declare function writeExternalAgentInstallation(installation: ExternalAgentInstallation, secret: ExternalAgentInstallationSecret, backend?: InstallationSecretBackend): Promise<void>;
69
112
  declare function readExternalAgentInstallation(environment: TopoloRuntimeEnvironment, id: string, backend?: InstallationSecretBackend): Promise<{
@@ -73,6 +116,13 @@ declare function readExternalAgentInstallation(environment: TopoloRuntimeEnviron
73
116
  declare function listExternalAgentInstallations(environment: TopoloRuntimeEnvironment): Promise<ExternalAgentInstallation[]>;
74
117
  declare function deleteExternalAgentInstallation(environment: TopoloRuntimeEnvironment, id: string, backend?: InstallationSecretBackend): Promise<void>;
75
118
 
119
+ declare function createProtectedActionSecretStore(options: {
120
+ environment: TopoloRuntimeEnvironment;
121
+ identity: () => Promise<CredentialIntrospection>;
122
+ backend?: InstallationSecretBackend;
123
+ now?: () => number;
124
+ }): TopoloActionSecretStore;
125
+
76
126
  /**
77
127
  * Node-only credential store shared by the `topolo` CLI and `@topolo/mcp`.
78
128
  *
@@ -166,4 +216,4 @@ declare function resolveServiceUrls(configUrls?: Record<string, string>, environ
166
216
  declare function assertServiceUrlForEnvironment(service: string, serviceUrl: string | undefined, environment?: TopoloEnvironment): void;
167
217
  declare function resolvedProfileConfig(config: TopoloConfig, environment?: TopoloEnvironment): TopoloEnvironmentProfile;
168
218
 
169
- export { ENVIRONMENT_DEFAULT_SERVICE_URLS, type ExternalAgentInstallation, type ExternalAgentInstallationSecret, type InstallationSecretBackend, TOPOLO_ENVIRONMENTS, type TopoloConfig, type TopoloContextCredential, type TopoloEnvironment, type TopoloEnvironmentProfile, activateContext, assertServiceUrlForEnvironment, assertTopoloServiceUrlEnvironment, clearProfileCredential, configPath, contextKey, createPlatformInstallationSecretBackend, credentialSourceFromConfig, deleteExternalAgentInstallation, envVarPrefix, installationStorePath, listExternalAgentInstallations, normalizeEnvironment, profileForEnvironment, readConfig, readExternalAgentInstallation, refreshContextCredentialIfNeeded, refreshCredentialIfNeeded, resolveAgentName, resolveCredential, resolveServiceUrls, resolvedProfileConfig, selectedEnvironment, topoloServiceOrigins, writeConfig, writeContextCredential, writeExternalAgentInstallation, writeProfileConfig };
219
+ export { ENVIRONMENT_DEFAULT_SERVICE_URLS, type ExternalAgentInstallation, type ExternalAgentInstallationSecret, type InstallationSecretBackend, TOPOLO_ENVIRONMENTS, type TopoloConfig, type TopoloContextCredential, type TopoloEnvironment, type TopoloEnvironmentProfile, activateContext, assertServiceUrlForEnvironment, assertTopoloServiceUrlEnvironment, clearProfileCredential, configPath, contextKey, createPlatformInstallationSecretBackend, createProtectedActionSecretStore, credentialSourceFromConfig, deleteExternalAgentInstallation, envVarPrefix, installationStorePath, listExternalAgentInstallations, normalizeEnvironment, profileForEnvironment, readConfig, readExternalAgentInstallation, refreshContextCredentialIfNeeded, refreshCredentialIfNeeded, resolveAgentName, resolveCredential, resolveServiceUrls, resolvedProfileConfig, selectedEnvironment, topoloServiceOrigins, writeConfig, writeContextCredential, writeExternalAgentInstallation, writeProfileConfig };
@@ -26,11 +26,53 @@ type TopoloCredential = {
26
26
  expiresAt?: number;
27
27
  };
28
28
 
29
+ interface TopoloActionSecretStore {
30
+ save(value: unknown): Promise<string>;
31
+ load(reference: string): Promise<unknown>;
32
+ delete(reference: string): Promise<void>;
33
+ }
34
+
29
35
  declare const TOPOLO_RUNTIME_ENVIRONMENTS: readonly ["production", "staging", "development", "local"];
30
36
  type TopoloRuntimeEnvironment = (typeof TOPOLO_RUNTIME_ENVIRONMENTS)[number];
31
37
  declare function assertTopoloServiceUrlEnvironment(service: string, value: string, environment: TopoloRuntimeEnvironment): string;
32
38
  declare function topoloServiceOrigins(serviceUrls: Record<string, string> | undefined, environment: TopoloRuntimeEnvironment): Record<string, string>;
33
39
 
40
+ interface CredentialIntrospection {
41
+ kind: 'api_key' | 'access_token';
42
+ authorizationMode: 'context_rbac' | 'oauth_scopes' | 'session_rbac' | 'api_key' | 'unknown';
43
+ user: {
44
+ id: string;
45
+ email: string;
46
+ name: string | null;
47
+ role: string;
48
+ permissions: string[];
49
+ principalId?: string | null;
50
+ principalType?: 'agent_employee' | 'external_agent' | null;
51
+ accountableUserId?: string | null;
52
+ agentInstanceId?: string | null;
53
+ installationId?: string | null;
54
+ provider?: string | null;
55
+ model?: string | null;
56
+ serviceScopes?: string[];
57
+ policyRef?: string | null;
58
+ };
59
+ organization: {
60
+ id: string;
61
+ slug: string;
62
+ name: string;
63
+ } | null;
64
+ apiKey: {
65
+ id: string | null;
66
+ name: string | null;
67
+ appId: string | null;
68
+ scopes: string[];
69
+ resourceBindings: Array<{
70
+ resourceType: string;
71
+ resourceId: string;
72
+ }>;
73
+ } | null;
74
+ }
75
+
34
76
  interface ExternalAgentInstallation {
35
77
  id: string;
36
78
  environment: TopoloRuntimeEnvironment;
@@ -64,6 +106,7 @@ declare function createPlatformInstallationSecretBackend(options?: {
64
106
  platform?: NodeJS.Platform;
65
107
  spawn?: Spawn;
66
108
  root?: string;
109
+ service?: string;
67
110
  }): InstallationSecretBackend;
68
111
  declare function writeExternalAgentInstallation(installation: ExternalAgentInstallation, secret: ExternalAgentInstallationSecret, backend?: InstallationSecretBackend): Promise<void>;
69
112
  declare function readExternalAgentInstallation(environment: TopoloRuntimeEnvironment, id: string, backend?: InstallationSecretBackend): Promise<{
@@ -73,6 +116,13 @@ declare function readExternalAgentInstallation(environment: TopoloRuntimeEnviron
73
116
  declare function listExternalAgentInstallations(environment: TopoloRuntimeEnvironment): Promise<ExternalAgentInstallation[]>;
74
117
  declare function deleteExternalAgentInstallation(environment: TopoloRuntimeEnvironment, id: string, backend?: InstallationSecretBackend): Promise<void>;
75
118
 
119
+ declare function createProtectedActionSecretStore(options: {
120
+ environment: TopoloRuntimeEnvironment;
121
+ identity: () => Promise<CredentialIntrospection>;
122
+ backend?: InstallationSecretBackend;
123
+ now?: () => number;
124
+ }): TopoloActionSecretStore;
125
+
76
126
  /**
77
127
  * Node-only credential store shared by the `topolo` CLI and `@topolo/mcp`.
78
128
  *
@@ -166,4 +216,4 @@ declare function resolveServiceUrls(configUrls?: Record<string, string>, environ
166
216
  declare function assertServiceUrlForEnvironment(service: string, serviceUrl: string | undefined, environment?: TopoloEnvironment): void;
167
217
  declare function resolvedProfileConfig(config: TopoloConfig, environment?: TopoloEnvironment): TopoloEnvironmentProfile;
168
218
 
169
- export { ENVIRONMENT_DEFAULT_SERVICE_URLS, type ExternalAgentInstallation, type ExternalAgentInstallationSecret, type InstallationSecretBackend, TOPOLO_ENVIRONMENTS, type TopoloConfig, type TopoloContextCredential, type TopoloEnvironment, type TopoloEnvironmentProfile, activateContext, assertServiceUrlForEnvironment, assertTopoloServiceUrlEnvironment, clearProfileCredential, configPath, contextKey, createPlatformInstallationSecretBackend, credentialSourceFromConfig, deleteExternalAgentInstallation, envVarPrefix, installationStorePath, listExternalAgentInstallations, normalizeEnvironment, profileForEnvironment, readConfig, readExternalAgentInstallation, refreshContextCredentialIfNeeded, refreshCredentialIfNeeded, resolveAgentName, resolveCredential, resolveServiceUrls, resolvedProfileConfig, selectedEnvironment, topoloServiceOrigins, writeConfig, writeContextCredential, writeExternalAgentInstallation, writeProfileConfig };
219
+ export { ENVIRONMENT_DEFAULT_SERVICE_URLS, type ExternalAgentInstallation, type ExternalAgentInstallationSecret, type InstallationSecretBackend, TOPOLO_ENVIRONMENTS, type TopoloConfig, type TopoloContextCredential, type TopoloEnvironment, type TopoloEnvironmentProfile, activateContext, assertServiceUrlForEnvironment, assertTopoloServiceUrlEnvironment, clearProfileCredential, configPath, contextKey, createPlatformInstallationSecretBackend, createProtectedActionSecretStore, credentialSourceFromConfig, deleteExternalAgentInstallation, envVarPrefix, installationStorePath, listExternalAgentInstallations, normalizeEnvironment, profileForEnvironment, readConfig, readExternalAgentInstallation, refreshContextCredentialIfNeeded, refreshCredentialIfNeeded, resolveAgentName, resolveCredential, resolveServiceUrls, resolvedProfileConfig, selectedEnvironment, topoloServiceOrigins, writeConfig, writeContextCredential, writeExternalAgentInstallation, writeProfileConfig };