@keemakr/operator-sdk 0.3.0 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keemakr/operator-sdk",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "The keemakr connector contract: defineProvider + ConnectionProvider/ConnectionOperation types + OperationContext (governed fetch) + the connector manifest schema, shared by keemakr-core's built-in adapters and external connector repos. One shape, two consumers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -68,6 +68,15 @@ export async function runConformance(
68
68
  !manifest.auth_kind || provider.authKind === manifest.auth_kind,
69
69
  `code '${provider.authKind}' vs manifest '${manifest.auth_kind ?? '—'}'`,
70
70
  );
71
+ // The credential alias is a reviewed dependency: when either side declares
72
+ // it, both must, and they must agree (a code-only alias would ride a
73
+ // connection reviewers never saw).
74
+ add(
75
+ 'credential-alias',
76
+ 'credential alias matches the manifest (when either declares it)',
77
+ (provider.credentialProvider ?? null) === (manifest.credential_provider ?? null),
78
+ `code '${provider.credentialProvider ?? '—'}' vs manifest '${manifest.credential_provider ?? '—'}'`,
79
+ );
71
80
 
72
81
  // 6 — code may not claim MORE network than the reviewed manifest.
73
82
  const undeclared = (provider.allowedDomains ?? []).filter(
package/src/index.ts CHANGED
@@ -100,6 +100,24 @@ export interface ConnectField {
100
100
  help?: string;
101
101
  }
102
102
 
103
+ // Outcome of a connect-time (or on-demand) health probe. `status` is the user-
104
+ // facing vocabulary the Hub renders: 'connected' (probe passed), 'needs_rescope'
105
+ // (OAuth completed but the granted scopes can't run this provider's operations —
106
+ // e.g. Google's granular-consent checkboxes were left unticked), 'no_mailbox'
107
+ // (the account exists but has no mailbox/service behind it — a Workspace account
108
+ // on a Microsoft-mail domain), 'unusable' (probe failed for another reason).
109
+ export interface ProbeResult {
110
+ status: 'connected' | 'needs_rescope' | 'no_mailbox' | 'unusable';
111
+ /** One human-readable sentence for the Hub's status chip. */
112
+ detail?: string;
113
+ /** Scopes the credential actually carries, when the probe can read them. */
114
+ grantedScopes?: string[];
115
+ /** Probed account identity (e.g. the mailbox email) — becomes account_label. */
116
+ accountLabel?: string | null;
117
+ /** Extra provider facts worth persisting under meta.probe. */
118
+ meta?: Record<string, unknown>;
119
+ }
120
+
103
121
  export interface ConnectionProvider {
104
122
  provider: string;
105
123
  displayName: string;
@@ -112,6 +130,29 @@ export interface ConnectionProvider {
112
130
  // Connect-path maturity. Omitted ⇒ 'ready'. See Maturity above.
113
131
  maturity?: Maturity;
114
132
 
133
+ // NOTE: a connection's scope (personal 'user' vs org-shared 'tenant') is NOT
134
+ // declared on the adapter. The live value is connections_registry.connection_mode
135
+ // (the platform-controlled overlay + rollback lever). Marketplace connectors
136
+ // seed it by declaring `connection_mode` in the MANIFEST (first publish only —
137
+ // the registry column is authoritative afterward, and reclassifying a live
138
+ // provider is a guarded staff console action). Built-ins still seed from
139
+ // registry-state's DEFAULT_USER_MODE_PROVIDERS, which stays the fail-closed
140
+ // fallback for unreadable registry reads.
141
+
142
+ // OAuth scopes this provider's operations actually NEED — unlike `scopes`
143
+ // (display-only), these are compared against the granted scope string by the
144
+ // connect-time probe; missing entries mark the connection 'needs_rescope'.
145
+ requiredScopes?: string[];
146
+
147
+ // Connect-time health probe: one cheap read (a profile fetch) that proves the
148
+ // credential can actually operate, run after the OAuth reports success and on
149
+ // demand from the Hub's "Check connection". Omitted ⇒ the connection is
150
+ // trusted as-is (status stays whatever the OAuth layer reported).
151
+ probe?(
152
+ cred: { accessToken: string; meta: Record<string, unknown> },
153
+ ctx?: OperationContext,
154
+ ): Promise<ProbeResult>;
155
+
115
156
  // Audience classification — see ConnectorSurface. Drives whether the end-user
116
157
  // client surfaces this connector and how. Optional in the type for now so
117
158
  // existing/external adapters compile through the SDK bump; defineProvider warns
@@ -130,6 +171,17 @@ export interface ConnectionProvider {
130
171
  // omit it (first-party).
131
172
  publisher?: string;
132
173
 
174
+ // Credential alias — the agent-tool chain's "one key opens many doors" seam.
175
+ // A connector that fronts one facet of a wider platform (an Apify actor, a
176
+ // Composio toolkit) declares the provider whose TENANT CONNECTION it rides:
177
+ // when a tenant has no row for THIS provider, getConnection falls back to the
178
+ // alias provider's row (tenant connects 'apify' once; every actor connector
179
+ // works). A tenant-pasted key for this provider still wins — the alias is the
180
+ // fallback, not the override. Refresh semantics follow the ALIAS provider's
181
+ // adapter. One hop only; the alias must name a concrete provider, never
182
+ // another aliased one.
183
+ credentialProvider?: string;
184
+
133
185
  // Inline connect-form spec for api_key / app_password shapes. Omitted by pure
134
186
  // oauth2 adapters (they render a Connect button instead). For a dual-mode
135
187
  // provider (WordPress), this describes the NON-oauth fallback form; the UI
@@ -193,6 +245,9 @@ export interface ConnectionProvider {
193
245
  // sees it. Keep `call` pure-ish: do the request, return JSON-serializable data.
194
246
  export interface ConnectionOperation {
195
247
  description?: string;
248
+ // Platform-only provider primitive. It remains available to trusted internal callers that
249
+ // invoke the adapter directly, but is omitted from catalogs and agent/user operation surfaces.
250
+ internal?: boolean;
196
251
  // Access class, for grant scoping + install consent. Omitted ⇒ 'write' (the
197
252
  // most restrictive assumption: unlabeled ops require the write scope and are
198
253
  // listed as writes on the consent screen).
package/src/manifest.ts CHANGED
@@ -46,6 +46,19 @@ export const connectorManifestSchema = z
46
46
  // publish gate. Optional (the adapter is authoritative); declaring it lets
47
47
  // the upload preview show it before any code runs.
48
48
  auth_kind: z.enum(['oauth2', 'api_key', 'app_password']).optional(),
49
+ // OWNERSHIP axis (orthogonal to `surface`): 'user' = each member connects
50
+ // their OWN account (one row per member, invisible to everyone else — a
51
+ // personal mailbox/drive/calendar); 'tenant' = one org-shared connection.
52
+ // Declare 'user' whenever the credential is one person's own account.
53
+ //
54
+ // Contract: this value SEEDS connections_registry.connection_mode at FIRST
55
+ // publish only — the platform column is authoritative afterward. Changing
56
+ // it in a later version raises a publish-diff warning and does NOT apply;
57
+ // live reclassification is a guarded staff console action (adopt/clean-cut).
58
+ // NEVER make this required, and never make the schema strict: stored
59
+ // manifests of already-published versions are re-parsed with the current
60
+ // schema at every publish, and only a defaulted optional field survives that.
61
+ connection_mode: z.enum(['tenant', 'user']).default('tenant'),
49
62
  // The operator-sdk contract version the connector targets (semver).
50
63
  sdk_version: z.string().regex(/^\d+\.\d+\.\d+$/, 'sdk_version must be exact semver'),
51
64
  // Module (relative path inside the bundle) whose named export is the
@@ -92,6 +105,14 @@ export const connectorManifestSchema = z
92
105
  allowed_domains: z
93
106
  .array(z.string().regex(domainPattern, 'each entry is a bare host or *.host, optional :port'))
94
107
  .min(1, 'declare at least one allowed domain'),
108
+ // Credential alias (see ConnectionProvider.credentialProvider): the provider
109
+ // whose tenant connection this connector rides when the tenant has not
110
+ // connected THIS provider directly. Declared here so reviewers see the
111
+ // dependency; the adapter's copy is authoritative at runtime.
112
+ credential_provider: z
113
+ .string()
114
+ .regex(kebab, 'credential_provider must be a lowercase-kebab provider key')
115
+ .optional(),
95
116
  // Connector-level config keys the platform must supply (resolved from the
96
117
  // encrypted config store — values are set by staff at publish, never bundled).
97
118
  env: z