@flowy-agent-store/client 0.1.0-beta.7 → 0.1.0-beta.9

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.cjs CHANGED
@@ -276,7 +276,7 @@ var WebSocketTransport = class {
276
276
  /** Header the server mints the per-call connection id under. */
277
277
  const CONNECTION_HEADER = "x-app-server-connection-id";
278
278
  /** Protocol version this binding announces on the one-shot handshake. */
279
- const PROTOCOL_VERSION = "fp-8";
279
+ const PROTOCOL_VERSION = "fp-13";
280
280
  /**
281
281
  * Protocol method → REST route. Verified from handler bodies, not from the
282
282
  * route list (a route existing does not prove which method it mirrors).
@@ -471,6 +471,26 @@ const HTTP_ROUTES = {
471
471
  path: "/connectors/:connector_id/auth-logout",
472
472
  source: "connector_auth_logout_route() -> connector_auth_logout_impl"
473
473
  },
474
+ "connector/credential/get": {
475
+ verb: "GET",
476
+ path: "/connectors/:connector_id/credential",
477
+ source: "connector_credential_get_route() -> connector_credential_get_impl"
478
+ },
479
+ "connector/credential/set": {
480
+ verb: "POST",
481
+ path: "/connectors/:connector_id/credential",
482
+ source: "connector_credential_set_route() -> connector_credential_set_impl"
483
+ },
484
+ "connector/credential/clear": {
485
+ verb: "POST",
486
+ path: "/connectors/:connector_id/credential/clear",
487
+ source: "connector_credential_clear_route() -> connector_credential_clear_impl"
488
+ },
489
+ "connector/register": {
490
+ verb: "POST",
491
+ path: "/connectors",
492
+ source: "register_connector_route() -> register_connector_impl"
493
+ },
474
494
  "import/run": {
475
495
  verb: "POST",
476
496
  path: "/imports",
@@ -551,6 +571,17 @@ const HTTP_ROUTES = {
551
571
  path: "/markets/:marketplace_id/entries/:entry_name/import",
552
572
  source: "market_entry_import_route() -> market_entry_import_impl"
553
573
  },
574
+ "market/settings": {
575
+ verb: "GET",
576
+ path: "/market-settings",
577
+ source: "market_settings_route() -> market_settings_impl"
578
+ },
579
+ "market/settings-set": {
580
+ verb: "POST",
581
+ path: "/market-settings",
582
+ body: ["auto_update_interval_hours", "entry_auto_update_kinds"],
583
+ source: "market_settings_set_route() -> market_settings_set_impl"
584
+ },
554
585
  "store/list": {
555
586
  verb: "GET",
556
587
  path: "/store",
@@ -560,6 +591,11 @@ const HTTP_ROUTES = {
560
591
  verb: "POST",
561
592
  path: "/store/:marketplace_id/entries/:entry_name/install",
562
593
  source: "store_install_entry_route() -> store_install_entry_impl"
594
+ },
595
+ "store/update-entry": {
596
+ verb: "POST",
597
+ path: "/store/:marketplace_id/entries/:entry_name/update",
598
+ source: "store_update_entry_route() -> store_update_entry_impl"
563
599
  }
564
600
  };
565
601
  /** Methods that are WebSocket-only by design — named so the error explains itself. */
@@ -1080,6 +1116,13 @@ var ConversationSubscription = class {
1080
1116
  };
1081
1117
  //#endregion
1082
1118
  //#region src/connectors.ts
1119
+ /**
1120
+ * How long {@link ConnectorClient.waitForAuth} waits by default: the host's own
1121
+ * callback window is 120s, so a shorter client budget would give up on flows the
1122
+ * host is still willing to finish.
1123
+ */
1124
+ const DEFAULT_AUTH_TIMEOUT_MS = 12e4;
1125
+ const DEFAULT_AUTH_POLL_MS = 500;
1083
1126
  var ConnectorClient = class {
1084
1127
  transport;
1085
1128
  constructor(transport) {
@@ -1113,17 +1156,117 @@ var ConnectorClient = class {
1113
1156
  test(connectorId) {
1114
1157
  return this.transport.request("connector/test", { connector_id: connectorId });
1115
1158
  }
1116
- /** Check the current OAuth state for a connector. */
1159
+ /**
1160
+ * Check the current OAuth state for a connector.
1161
+ *
1162
+ * Read `error` too: a flow that failed after the browser step is reported as
1163
+ * `state: "not_authenticated"` **plus** `error`, because the wire has no
1164
+ * failing state to flip to. {@link waitForAuth} is the loop that does this.
1165
+ */
1117
1166
  authStatus(connectorId) {
1118
1167
  return this.transport.request("connector/auth/status", { connector_id: connectorId });
1119
1168
  }
1120
1169
  /**
1170
+ * The credential form a connector needs filled in, as **this caller** sees it
1171
+ * (doc `34` §6.1).
1172
+ *
1173
+ * `mode` says which kind of authentication applies (`none` / `oauth` / `token`),
1174
+ * `status` whether anything is still missing, and `missing` names the keys.
1175
+ * `fields` is everything needed to render the form — labels, placeholders,
1176
+ * descriptions and the marketplace's "where do I get a key" link, in both
1177
+ * languages. No secret ever comes back: only `plain` fields carry a `value`.
1178
+ */
1179
+ credentials(connectorId) {
1180
+ return this.transport.request("connector/credential/get", { connector_id: connectorId });
1181
+ }
1182
+ /**
1183
+ * Store what the user typed and get the new state back.
1184
+ *
1185
+ * Only keys the connector's own declaration names are accepted — the form is
1186
+ * the whole write surface. `secret` fields go to the host's credential store
1187
+ * under the caller's own namespace, `plain` fields into the connector's
1188
+ * configuration, so a shared host never resolves one user's token for another.
1189
+ */
1190
+ setCredentials(connectorId, values) {
1191
+ return this.transport.request("connector/credential/set", {
1192
+ connector_id: connectorId,
1193
+ values
1194
+ });
1195
+ }
1196
+ /**
1197
+ * Forget stored credentials. `keys` omitted = every secret field of this
1198
+ * connector. Idempotent: clearing what is not there succeeds.
1199
+ */
1200
+ clearCredentials(connectorId, keys) {
1201
+ return this.transport.request("connector/credential/clear", {
1202
+ connector_id: connectorId,
1203
+ ...keys ? { keys } : {}
1204
+ });
1205
+ }
1206
+ /**
1207
+ * Register a connector this host never imported — your own MCP server, handed
1208
+ * over together with the template that says what it needs (doc `34` §6.5).
1209
+ *
1210
+ * The **template is the whole declaration**: whatever `${secret:NAME}` the URL,
1211
+ * headers or env name becomes that connector's credential form, so there is no
1212
+ * `token-schema.json` to ship and nothing to install from a marketplace. The
1213
+ * connector's own non-secret settings travel in `values`.
1214
+ *
1215
+ * Secrets do **not** travel here: hand them over with {@link setCredentials},
1216
+ * which keeps exactly one write surface for values. The returned detail already
1217
+ * carries the credential block, so `missing` tells you which keys are still
1218
+ * needed.
1219
+ *
1220
+ * Re-registering the same name updates it; a builtin server's name is refused.
1221
+ * The row is created **disabled** — enabling it still requires a probe that
1222
+ * passes — and this method is on the installation-owner-only surface, because
1223
+ * choosing where the host reaches is the owner's call.
1224
+ */
1225
+ register(registration) {
1226
+ return this.transport.request("connector/register", registration);
1227
+ }
1228
+ /**
1121
1229
  * Kick off the OAuth browser flow on the trusted host. Returns the start
1122
- * acknowledgement immediately; poll `authStatus` until `authenticated`.
1230
+ * acknowledgement immediately; follow it with {@link waitForAuth}.
1231
+ *
1232
+ * `state: "error"` here is a failure that happened **before** the browser was
1233
+ * opened (endpoint discovery, client identity, binding the callback, launching
1234
+ * the browser): nothing was shown to the user, so `error` is the whole story
1235
+ * and there is nothing to wait for.
1123
1236
  */
1124
1237
  authStart(connectorId) {
1125
1238
  return this.transport.request("connector/auth/start", { connector_id: connectorId });
1126
1239
  }
1240
+ /**
1241
+ * Wait for an authorization that was started (in the browser, or by a
1242
+ * previous `authStart`) to finish — and **report why** when it does not.
1243
+ *
1244
+ * A hand-rolled `while (state !== "authenticated") poll()` loop ends in "it
1245
+ * never became authenticated", with the reason sitting unread in the status it
1246
+ * already fetched: a token exchange the authorization server refused (a
1247
+ * throttling gateway answers `slow_down`), a callback timeout, a CSRF/path
1248
+ * mismatch. Those land in `authStatus().error` *only*, and the state stays
1249
+ * `not_authenticated` — there is no failing state on the wire.
1250
+ *
1251
+ * Resolves for every domain outcome (`authenticated` / `error` / `timeout`);
1252
+ * rejects only when the status read itself fails (transport or protocol), the
1253
+ * same rule as `connector/call`. The wait is bounded by `timeoutMs`; a caller
1254
+ * that needs to stop earlier can race the returned promise.
1255
+ */
1256
+ async waitForAuth(connectorId, options = {}) {
1257
+ const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_AUTH_TIMEOUT_MS);
1258
+ const pollMs = options.pollMs ?? DEFAULT_AUTH_POLL_MS;
1259
+ for (;;) {
1260
+ const status = await this.authStatus(connectorId);
1261
+ if (status.state === "authenticated") return { state: "authenticated" };
1262
+ if (status.error) return {
1263
+ state: "error",
1264
+ error: status.error
1265
+ };
1266
+ if (Date.now() >= deadline) return { state: "timeout" };
1267
+ await new Promise((resolve) => setTimeout(resolve, pollMs));
1268
+ }
1269
+ }
1127
1270
  /** Revoke the OAuth token for a connector. */
1128
1271
  logout(connectorId) {
1129
1272
  return this.transport.request("connector/auth/logout", { connector_id: connectorId }).then(() => void 0);
@@ -1543,6 +1686,7 @@ var StoreError = class extends Error {
1543
1686
  const DEFAULT_READY_TIMEOUT_MS = 3e4;
1544
1687
  const DEFAULT_READY_POLL_MS = 400;
1545
1688
  const DEFAULT_READY_POLL_MAX_MS = 4e3;
1689
+ const DEFAULT_READY_PROBE_MS = 5e3;
1546
1690
  /** Every string a `LocalizedText` carries, for haystack building. */
1547
1691
  function localizedValues(text) {
1548
1692
  if (typeof text === "string") return [text];
@@ -1584,11 +1728,13 @@ var StoreClient = class {
1584
1728
  readyTimeoutMs;
1585
1729
  readyPollMs;
1586
1730
  readyPollMaxMs;
1731
+ readyProbeMs;
1587
1732
  constructor(host, options = {}) {
1588
1733
  this.host = host;
1589
1734
  this.readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS;
1590
1735
  this.readyPollMs = options.readyPollMs ?? DEFAULT_READY_POLL_MS;
1591
1736
  this.readyPollMaxMs = options.readyPollMaxMs ?? DEFAULT_READY_POLL_MAX_MS;
1737
+ this.readyProbeMs = options.readyProbeMs ?? DEFAULT_READY_PROBE_MS;
1592
1738
  }
1593
1739
  /** Every item across enabled marketplaces, with install state. */
1594
1740
  async list() {
@@ -1620,15 +1766,45 @@ var StoreClient = class {
1620
1766
  /**
1621
1767
  * What a caller can offer for an item's pending update.
1622
1768
  *
1623
- * `"none"` means there is nothing to do; `"uninstall_reinstall"` means the
1624
- * entry is installed at a different version and the only path the wire offers
1625
- * is to release it and install again (the version-aware re-import then picks
1626
- * up the current version). `"unknown"` is returned rather than guessing when
1627
- * the item carries no snapshot to release.
1769
+ * `"none"` means there is nothing to do; `"update"` means the entry is
1770
+ * installed at a different version and `update()` is the action that lands it
1771
+ * (`store/update-entry`, `36` D3); `"unknown"` is returned rather than
1772
+ * guessing when the item carries no snapshot to replace.
1628
1773
  */
1629
1774
  updateHint(item) {
1630
1775
  if (!item.installed || !item.update_available) return "none";
1631
- return item.snapshot_id ? "uninstall_reinstall" : "unknown";
1776
+ return item.snapshot_id ? "update" : "unknown";
1777
+ }
1778
+ /**
1779
+ * Upgrade an installed item to the version its marketplace advertises.
1780
+ *
1781
+ * The host does the ordering: new version installed first, previous one
1782
+ * released only when that succeeded. So a failure resolves `ok: false` with
1783
+ * the previous installation still in place (`releasedCount === 0`) — the
1784
+ * caller never has to reason about a half-upgraded item.
1785
+ *
1786
+ * Refuses before the round trip when there is nothing to replace: an item that
1787
+ * is not installed (`not_installed`) or is already current is not a failure of
1788
+ * *this* call, and `updateHint()` is the way to ask first.
1789
+ */
1790
+ async update(item, options = {}) {
1791
+ this.assertUsable(item, options.signal);
1792
+ this.requireSnapshot(item);
1793
+ const result = await this.host.updateStoreEntry(item.marketplace_id, item.entry_name);
1794
+ const components = result.outcomes ?? [];
1795
+ const outcome = {
1796
+ snapshotId: result.snapshot_id,
1797
+ reused: result.reused,
1798
+ components,
1799
+ ok: result.errors.length === 0 && components.every((component) => component.ok),
1800
+ errors: result.errors,
1801
+ warnings: result.warnings,
1802
+ fromVersion: result.previous_version ?? void 0,
1803
+ toVersion: result.version,
1804
+ releasedCount: result.released_count ?? 0
1805
+ };
1806
+ if (options.waitForReady === false) return outcome;
1807
+ return this.awaitReady(outcome, options);
1632
1808
  }
1633
1809
  /**
1634
1810
  * Import (when needed) and install one store item, then wait until it is
@@ -1642,7 +1818,9 @@ var StoreClient = class {
1642
1818
  snapshotId: result.snapshot_id,
1643
1819
  reused: result.reused,
1644
1820
  components,
1645
- ok: result.errors.length === 0 && components.every((component) => component.ok)
1821
+ ok: result.errors.length === 0 && components.every((component) => component.ok),
1822
+ errors: result.errors,
1823
+ warnings: result.warnings
1646
1824
  };
1647
1825
  if (options.waitForReady === false) return outcome;
1648
1826
  return this.awaitReady(outcome, options);
@@ -1661,7 +1839,9 @@ var StoreClient = class {
1661
1839
  snapshotId,
1662
1840
  reused: true,
1663
1841
  components: [],
1664
- ok: true
1842
+ ok: true,
1843
+ errors: [],
1844
+ warnings: []
1665
1845
  };
1666
1846
  const status = await this.host.uninstallInstall(snapshotId, componentIds);
1667
1847
  return this.outcomeFromStatus(snapshotId, status, false);
@@ -1681,7 +1861,9 @@ var StoreClient = class {
1681
1861
  snapshotId,
1682
1862
  reused: true,
1683
1863
  components: [],
1684
- ok: true
1864
+ ok: true,
1865
+ errors: [],
1866
+ warnings: []
1685
1867
  };
1686
1868
  const status = enabled ? await this.host.enableInstall(snapshotId, componentIds) : await this.host.disableInstall(snapshotId, componentIds);
1687
1869
  return this.outcomeFromStatus(snapshotId, status, false);
@@ -1700,11 +1882,14 @@ var StoreClient = class {
1700
1882
  }
1701
1883
  outcomeFromStatus(snapshotId, status, reused) {
1702
1884
  const components = status.outcomes ?? [];
1885
+ const errors = status.errors ?? [];
1703
1886
  return {
1704
1887
  snapshotId,
1705
1888
  reused,
1706
1889
  components,
1707
- ok: (status.errors ?? []).length === 0 && components.every((component) => component.ok)
1890
+ ok: errors.length === 0 && components.every((component) => component.ok),
1891
+ errors,
1892
+ warnings: []
1708
1893
  };
1709
1894
  }
1710
1895
  /**
@@ -1722,11 +1907,15 @@ var StoreClient = class {
1722
1907
  if (connectors.length > 0) await this.host.enableInstall(snapshotId, connectors.map((component) => component.id));
1723
1908
  const deadline = Date.now() + (options.timeoutMs ?? this.readyTimeoutMs);
1724
1909
  let interval = this.readyPollMs;
1910
+ let nextProbeAt = 0;
1725
1911
  let last = status.components;
1726
1912
  for (;;) {
1913
+ const now = Date.now();
1914
+ const allowProbe = now >= nextProbeAt;
1915
+ if (allowProbe) nextProbeAt = now + this.readyProbeMs;
1727
1916
  const blocked = (await Promise.all(last.filter((component) => component.state !== "not-installed").map(async (component) => ({
1728
1917
  component,
1729
- verdict: await this.readiness(component)
1918
+ verdict: await this.readiness(component, allowProbe)
1730
1919
  })))).find((entry) => !entry.verdict.ready);
1731
1920
  if (!blocked) return {
1732
1921
  ...outcome,
@@ -1756,8 +1945,12 @@ var StoreClient = class {
1756
1945
  * unknown kind is reported ready rather than blocking: this layer cannot judge
1757
1946
  * a component it does not understand, and a false timeout on a future kind
1758
1947
  * would be worse than passing it through.
1948
+ *
1949
+ * `allowProbe` gates the one kind whose readiness needs a real connection (a
1950
+ * connector); the other kinds answer from local reads, so the cadence does not
1951
+ * apply to them.
1759
1952
  */
1760
- async readiness(component) {
1953
+ async readiness(component, allowProbe) {
1761
1954
  try {
1762
1955
  switch (component.kind) {
1763
1956
  case "skill": return (await this.host.skills.list()).some((skill) => skill.name === component.name) ? { ready: true } : { ready: false };
@@ -1766,8 +1959,9 @@ var StoreClient = class {
1766
1959
  await this.host.teams.get(component.id);
1767
1960
  return { ready: true };
1768
1961
  case "connector": {
1769
- if ((await this.host.connectors.test(component.id)).success) return { ready: true };
1962
+ if (allowProbe && (await this.host.connectors.test(component.id)).success) return { ready: true };
1770
1963
  const status = await this.host.connectors.status(component.id);
1964
+ if (status.status === "connected") return { ready: true };
1771
1965
  if (status.status === "authorization_required" || status.status === "reauthorization_required") return {
1772
1966
  ready: false,
1773
1967
  terminal: "authorization_required"
@@ -2043,6 +2237,25 @@ var AppServerClient = class {
2043
2237
  entry_name: entryName
2044
2238
  });
2045
2239
  }
2240
+ /**
2241
+ * The host's market policy: sweep cadence, auto-upgrade kind whitelist and the
2242
+ * last sweep's reading (doc `37` §3.2).
2243
+ *
2244
+ * Host-level rather than per-market: it answers "when does the sweep run and
2245
+ * what may it touch", while {@link setMarketplaceAutoUpdate} answers "which
2246
+ * market participates".
2247
+ */
2248
+ async getMarketSettings() {
2249
+ return this.transport.request("market/settings", {});
2250
+ }
2251
+ /**
2252
+ * Update the host's market policy. An absent field is left alone; the host
2253
+ * writes the value into its `config.toml` and answers with the file re-read,
2254
+ * so a caller can never see an optimistic echo.
2255
+ */
2256
+ async setMarketSettings(patch) {
2257
+ return this.transport.request("market/settings-set", patch);
2258
+ }
2046
2259
  /** All store items across enabled marketplaces (display + install state). */
2047
2260
  async listStore() {
2048
2261
  return this.transport.request("store/list", {});
@@ -2054,6 +2267,20 @@ var AppServerClient = class {
2054
2267
  entry_name: entryName
2055
2268
  });
2056
2269
  }
2270
+ /**
2271
+ * Upgrade an installed entry to the version its marketplace advertises.
2272
+ *
2273
+ * The host installs the new version before releasing the old one, so a failure
2274
+ * leaves the previous installation usable (`released_count: 0` is the machine
2275
+ * readable form of that). Refused for an entry that is not installed — this is
2276
+ * never a second way to install something.
2277
+ */
2278
+ async updateStoreEntry(marketplaceId, entryName) {
2279
+ return this.transport.request("store/update-entry", {
2280
+ marketplace_id: marketplaceId,
2281
+ entry_name: entryName
2282
+ });
2283
+ }
2057
2284
  };
2058
2285
  //#endregion
2059
2286
  //#region src/conversation-handle.ts
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { AgentDetail, AgentRunInput, AgentSummary, AnswerDecisionInput, CancelRunInput, ClientCapabilities, ClientInfo, ConnectorCallResult, ConnectorDetail, ConnectorProbeResult, ConnectorStatusView, ConnectorSummary, ContextUsage, ConversationCreateInput, ConversationEvent, ConversationMessage, ConversationMessagesPage, ConversationMessagesQuery, ConversationModelOptions, ConversationSendReceipt, ConversationUpdateInput, ConversationView, ExpertPack, ImportDetail, ImportRequest, ImportResult, ImportSummary, InitializeResult, InstallOutcome, InstallRequest, InstallResult, InstallStatus, MarketplaceAddRequest, MarketplaceDetail, MarketplaceRefreshResult, MarketplaceRemoveResult, MarketplaceSummary, MentionRef, ModelSummary, OAuthStartResult, OAuthStatusView, ProviderWithModel, RunEvent, RunEventsQuery, RunPlan, RunReceipt, RunResult, RunStatus, RunView, ServerNotification, SkillDetail, SkillFileContent, SkillFileList, SkillSummary, SteerRunInput, StoreInstallResult, StoreItem, StoreItemKind, StoreList, TeamDetail, TeamRunInput, TeamRunReceipt, TeamSummary, WorkspaceRevokeResult, WorkspaceView } from "@flowy-agent-store/protocol";
1
+ import { AgentDetail, AgentRunInput, AgentSummary, AnswerDecisionInput, CancelRunInput, ClientCapabilities, ClientInfo, ConnectorCallResult, ConnectorCredential, ConnectorDetail, ConnectorProbeResult, ConnectorRegistration, ConnectorStatusView, ConnectorSummary, ContextUsage, ConversationCreateInput, ConversationEvent, ConversationMessage, ConversationMessagesPage, ConversationMessagesQuery, ConversationModelOptions, ConversationSendReceipt, ConversationUpdateInput, ConversationView, ExpertPack, ImportDetail, ImportRequest, ImportResult, ImportSummary, InitializeResult, InstallOutcome, InstallRequest, InstallResult, InstallStatus, MarketSettings, MarketSettingsPatch, MarketplaceAddRequest, MarketplaceDetail, MarketplaceRefreshResult, MarketplaceRemoveResult, MarketplaceSummary, MentionRef, ModelSummary, OAuthStartResult, OAuthStatusView, ProviderWithModel, RunEvent, RunEventsQuery, RunPlan, RunReceipt, RunResult, RunStatus, RunView, ServerNotification, SkillDetail, SkillFileContent, SkillFileList, SkillSummary, SteerRunInput, StoreInstallResult, StoreItem, StoreItemKind, StoreList, TeamDetail, TeamRunInput, TeamRunReceipt, TeamSummary, WorkspaceRevokeResult, WorkspaceView } from "@flowy-agent-store/protocol";
2
2
  //#region src/transport.d.ts
3
3
  export type NotificationListener = (notification: ServerNotification) => void;
4
4
  /**
@@ -374,6 +374,28 @@ export declare class ConversationSubscription {
374
374
  }
375
375
  //#endregion
376
376
  //#region src/connectors.d.ts
377
+ export interface WaitForAuthOptions {
378
+ /** Overall budget in ms; defaults to `DEFAULT_AUTH_TIMEOUT_MS` (120s). */
379
+ timeoutMs?: number;
380
+ /** Poll interval in ms; defaults to 500. The read is a local DB projection. */
381
+ pollMs?: number;
382
+ }
383
+ /**
384
+ * How a completed wait ended.
385
+ *
386
+ * `error` carries the host's own sentence. It is the shape of every failure that
387
+ * happens *after* `authStart` acknowledged the browser step — the state stays
388
+ * `not_authenticated` (there is no "failed" state on the wire), so the reason is
389
+ * the only signal there is.
390
+ */
391
+ export type WaitForAuthOutcome = {
392
+ state: "authenticated";
393
+ } | {
394
+ state: "error";
395
+ error: string;
396
+ } | {
397
+ state: "timeout";
398
+ };
377
399
  export declare class ConnectorClient {
378
400
  private readonly transport;
379
401
  constructor(transport: Transport);
@@ -397,13 +419,86 @@ export declare class ConnectorClient {
397
419
  * the last segment of its wire method.
398
420
  */
399
421
  test(connectorId: string): Promise<ConnectorProbeResult>;
400
- /** Check the current OAuth state for a connector. */
422
+ /**
423
+ * Check the current OAuth state for a connector.
424
+ *
425
+ * Read `error` too: a flow that failed after the browser step is reported as
426
+ * `state: "not_authenticated"` **plus** `error`, because the wire has no
427
+ * failing state to flip to. {@link waitForAuth} is the loop that does this.
428
+ */
401
429
  authStatus(connectorId: string): Promise<OAuthStatusView>;
430
+ /**
431
+ * The credential form a connector needs filled in, as **this caller** sees it
432
+ * (doc `34` §6.1).
433
+ *
434
+ * `mode` says which kind of authentication applies (`none` / `oauth` / `token`),
435
+ * `status` whether anything is still missing, and `missing` names the keys.
436
+ * `fields` is everything needed to render the form — labels, placeholders,
437
+ * descriptions and the marketplace's "where do I get a key" link, in both
438
+ * languages. No secret ever comes back: only `plain` fields carry a `value`.
439
+ */
440
+ credentials(connectorId: string): Promise<ConnectorCredential>;
441
+ /**
442
+ * Store what the user typed and get the new state back.
443
+ *
444
+ * Only keys the connector's own declaration names are accepted — the form is
445
+ * the whole write surface. `secret` fields go to the host's credential store
446
+ * under the caller's own namespace, `plain` fields into the connector's
447
+ * configuration, so a shared host never resolves one user's token for another.
448
+ */
449
+ setCredentials(connectorId: string, values: Record<string, string>): Promise<ConnectorCredential>;
450
+ /**
451
+ * Forget stored credentials. `keys` omitted = every secret field of this
452
+ * connector. Idempotent: clearing what is not there succeeds.
453
+ */
454
+ clearCredentials(connectorId: string, keys?: string[]): Promise<ConnectorCredential>;
455
+ /**
456
+ * Register a connector this host never imported — your own MCP server, handed
457
+ * over together with the template that says what it needs (doc `34` §6.5).
458
+ *
459
+ * The **template is the whole declaration**: whatever `${secret:NAME}` the URL,
460
+ * headers or env name becomes that connector's credential form, so there is no
461
+ * `token-schema.json` to ship and nothing to install from a marketplace. The
462
+ * connector's own non-secret settings travel in `values`.
463
+ *
464
+ * Secrets do **not** travel here: hand them over with {@link setCredentials},
465
+ * which keeps exactly one write surface for values. The returned detail already
466
+ * carries the credential block, so `missing` tells you which keys are still
467
+ * needed.
468
+ *
469
+ * Re-registering the same name updates it; a builtin server's name is refused.
470
+ * The row is created **disabled** — enabling it still requires a probe that
471
+ * passes — and this method is on the installation-owner-only surface, because
472
+ * choosing where the host reaches is the owner's call.
473
+ */
474
+ register(registration: ConnectorRegistration): Promise<ConnectorDetail>;
402
475
  /**
403
476
  * Kick off the OAuth browser flow on the trusted host. Returns the start
404
- * acknowledgement immediately; poll `authStatus` until `authenticated`.
477
+ * acknowledgement immediately; follow it with {@link waitForAuth}.
478
+ *
479
+ * `state: "error"` here is a failure that happened **before** the browser was
480
+ * opened (endpoint discovery, client identity, binding the callback, launching
481
+ * the browser): nothing was shown to the user, so `error` is the whole story
482
+ * and there is nothing to wait for.
405
483
  */
406
484
  authStart(connectorId: string): Promise<OAuthStartResult>;
485
+ /**
486
+ * Wait for an authorization that was started (in the browser, or by a
487
+ * previous `authStart`) to finish — and **report why** when it does not.
488
+ *
489
+ * A hand-rolled `while (state !== "authenticated") poll()` loop ends in "it
490
+ * never became authenticated", with the reason sitting unread in the status it
491
+ * already fetched: a token exchange the authorization server refused (a
492
+ * throttling gateway answers `slow_down`), a callback timeout, a CSRF/path
493
+ * mismatch. Those land in `authStatus().error` *only*, and the state stays
494
+ * `not_authenticated` — there is no failing state on the wire.
495
+ *
496
+ * Resolves for every domain outcome (`authenticated` / `error` / `timeout`);
497
+ * rejects only when the status read itself fails (transport or protocol), the
498
+ * same rule as `connector/call`. The wait is bounded by `timeoutMs`; a caller
499
+ * that needs to stop earlier can race the returned promise.
500
+ */
501
+ waitForAuth(connectorId: string, options?: WaitForAuthOptions): Promise<WaitForAuthOutcome>;
407
502
  /** Revoke the OAuth token for a connector. */
408
503
  logout(connectorId: string): Promise<void>;
409
504
  /**
@@ -621,6 +716,20 @@ export interface StoreClientOptions {
621
716
  /** First readiness poll interval in ms; doubles up to `readyPollMaxMs`. */
622
717
  readyPollMs?: number;
623
718
  readyPollMaxMs?: number;
719
+ /**
720
+ * Minimum gap between two *real probes* of the same connector while waiting
721
+ * for readiness, in ms. Defaults to `DEFAULT_READY_PROBE_MS`.
722
+ *
723
+ * A connector's readiness cannot be read: the status is derived from the last
724
+ * probe, so someone has to run one — but a probe is a real connection
725
+ * (`initialize` + `tools/list`) that also resolves the stored token, which
726
+ * means a refresh request when that token is near expiry, plus one more on a
727
+ * 401. Probing once per poll (the poll starts at `readyPollMs` = 400ms) turned
728
+ * a 30s readiness budget into ~10 connections and up to ~20 token requests
729
+ * against the connector's authorization server — with nobody having asked for
730
+ * anything. Between probes the loop reads the (free, local) status instead.
731
+ */
732
+ readyProbeMs?: number;
624
733
  }
625
734
  export interface StoreOperationOutcome {
626
735
  /** The snapshot the operation acted on; `null` when nothing was imported. */
@@ -634,6 +743,18 @@ export interface StoreOperationOutcome {
634
743
  components: InstallOutcome[];
635
744
  /** Every reported component reached the requested state. */
636
745
  ok: boolean;
746
+ /**
747
+ * The server's own failure lines, verbatim.
748
+ *
749
+ * `ok: false` alone is not an answer: the host explains *why* (a digest
750
+ * conflict means the market changed content without raising the version; a
751
+ * `preset_update_failed` names the component). Dropping these is what made a
752
+ * refused import unexplainable in the UI (`18` §11 D10), so they are carried
753
+ * rather than folded into `ok`.
754
+ */
755
+ errors: string[];
756
+ /** The server's non-fatal notes, verbatim (e.g. a component left disabled). */
757
+ warnings: string[];
637
758
  /**
638
759
  * `undefined` when readiness was not checked. `true` when every component is
639
760
  * usable now; `false` when it is not (see `readyIssue`).
@@ -642,6 +763,16 @@ export interface StoreOperationOutcome {
642
763
  readyIssue?: StoreReadyIssue;
643
764
  /** The component that needs attention, when `readyIssue` is set. */
644
765
  readyComponentId?: string;
766
+ /** `update()` only: the version that was installed before this call. */
767
+ fromVersion?: string;
768
+ /** `update()` only: the version now installed. */
769
+ toVersion?: string;
770
+ /**
771
+ * `update()` only: how many components of the replaced snapshot were actually
772
+ * released. `0` means the previous installation was not touched — an update
773
+ * that failed, or a no-op.
774
+ */
775
+ releasedCount?: number;
645
776
  }
646
777
  /**
647
778
  * The slice of `AppServerClient` this layer orchestrates.
@@ -653,6 +784,7 @@ export interface StoreOperationOutcome {
653
784
  export interface StoreHost {
654
785
  listStore(): Promise<StoreList>;
655
786
  installStoreEntry(marketplaceId: string, entryName: string): Promise<StoreInstallResult>;
787
+ updateStoreEntry(marketplaceId: string, entryName: string): Promise<StoreInstallResult>;
656
788
  getInstallStatus(snapshotId: string): Promise<InstallStatus>;
657
789
  disableInstall(snapshotId: string, componentIds: string[]): Promise<InstallStatus>;
658
790
  enableInstall(snapshotId: string, componentIds: string[]): Promise<InstallStatus>;
@@ -676,6 +808,7 @@ export declare class StoreClient {
676
808
  private readonly readyTimeoutMs;
677
809
  private readonly readyPollMs;
678
810
  private readonly readyPollMaxMs;
811
+ private readonly readyProbeMs;
679
812
  constructor(host: StoreHost, options?: StoreClientOptions);
680
813
  /** Every item across enabled marketplaces, with install state. */
681
814
  list(): Promise<StoreItem[]>;
@@ -696,13 +829,25 @@ export declare class StoreClient {
696
829
  /**
697
830
  * What a caller can offer for an item's pending update.
698
831
  *
699
- * `"none"` means there is nothing to do; `"uninstall_reinstall"` means the
700
- * entry is installed at a different version and the only path the wire offers
701
- * is to release it and install again (the version-aware re-import then picks
702
- * up the current version). `"unknown"` is returned rather than guessing when
703
- * the item carries no snapshot to release.
832
+ * `"none"` means there is nothing to do; `"update"` means the entry is
833
+ * installed at a different version and `update()` is the action that lands it
834
+ * (`store/update-entry`, `36` D3); `"unknown"` is returned rather than
835
+ * guessing when the item carries no snapshot to replace.
704
836
  */
705
- updateHint(item: StoreItem): "none" | "uninstall_reinstall" | "unknown";
837
+ updateHint(item: StoreItem): "none" | "update" | "unknown";
838
+ /**
839
+ * Upgrade an installed item to the version its marketplace advertises.
840
+ *
841
+ * The host does the ordering: new version installed first, previous one
842
+ * released only when that succeeded. So a failure resolves `ok: false` with
843
+ * the previous installation still in place (`releasedCount === 0`) — the
844
+ * caller never has to reason about a half-upgraded item.
845
+ *
846
+ * Refuses before the round trip when there is nothing to replace: an item that
847
+ * is not installed (`not_installed`) or is already current is not a failure of
848
+ * *this* call, and `updateHint()` is the way to ask first.
849
+ */
850
+ update(item: StoreItem, options?: InstallOptions): Promise<StoreOperationOutcome>;
706
851
  /**
707
852
  * Import (when needed) and install one store item, then wait until it is
708
853
  * usable.
@@ -744,6 +889,10 @@ export declare class StoreClient {
744
889
  * unknown kind is reported ready rather than blocking: this layer cannot judge
745
890
  * a component it does not understand, and a false timeout on a future kind
746
891
  * would be worse than passing it through.
892
+ *
893
+ * `allowProbe` gates the one kind whose readiness needs a real connection (a
894
+ * connector); the other kinds answer from local reads, so the cadence does not
895
+ * apply to them.
747
896
  */
748
897
  private readiness;
749
898
  }
@@ -886,10 +1035,34 @@ export declare class AppServerClient {
886
1035
  refreshMarketplace(marketplaceId: string): Promise<MarketplaceRefreshResult>;
887
1036
  /** Import one discovered marketplace entry (provenance linked). */
888
1037
  importMarketplaceEntry(marketplaceId: string, entryName: string): Promise<ImportResult>;
1038
+ /**
1039
+ * The host's market policy: sweep cadence, auto-upgrade kind whitelist and the
1040
+ * last sweep's reading (doc `37` §3.2).
1041
+ *
1042
+ * Host-level rather than per-market: it answers "when does the sweep run and
1043
+ * what may it touch", while {@link setMarketplaceAutoUpdate} answers "which
1044
+ * market participates".
1045
+ */
1046
+ getMarketSettings(): Promise<MarketSettings>;
1047
+ /**
1048
+ * Update the host's market policy. An absent field is left alone; the host
1049
+ * writes the value into its `config.toml` and answers with the file re-read,
1050
+ * so a caller can never see an optimistic echo.
1051
+ */
1052
+ setMarketSettings(patch: MarketSettingsPatch): Promise<MarketSettings>;
889
1053
  /** All store items across enabled marketplaces (display + install state). */
890
1054
  listStore(): Promise<StoreList>;
891
1055
  /** One-click install: import (when missing) + register in one call. */
892
1056
  installStoreEntry(marketplaceId: string, entryName: string): Promise<StoreInstallResult>;
1057
+ /**
1058
+ * Upgrade an installed entry to the version its marketplace advertises.
1059
+ *
1060
+ * The host installs the new version before releasing the old one, so a failure
1061
+ * leaves the previous installation usable (`released_count: 0` is the machine
1062
+ * readable form of that). Refused for an entry that is not installed — this is
1063
+ * never a second way to install something.
1064
+ */
1065
+ updateStoreEntry(marketplaceId: string, entryName: string): Promise<StoreInstallResult>;
893
1066
  }
894
1067
  //#endregion
895
1068
  //#region src/conversation-handle.d.ts