OOMOL TypeScript SDK 參考
@oomol-lab/connector 包含三個 TypeScript client。請先按產品路徑選擇 client,再查閱本頁的共享設定與 API 參考:
Connector透過託管閘道呼叫你在 OOMOL 帳號中連接的帳號。ProjectConnector為 SaaS 產品的最終使用者連接並呼叫各自的帳號。OpenConnector呼叫由你維運的 OpenConnector runtime。
三個 client 共用傳輸行為、精確 action 型別和錯誤模型,但使用的 key、帳號邊界與可用方法不同。
這個套件輕量、零依賴;SDK 直接建構請求並解析回應。gmail.search_threads、slack.post_message、notion.append_block 等 action 可以直接透過 TypeScript 呼叫。
安裝
npm install @oomol-lab/connector # 或:bun add / pnpm add / yarn add
需要 Node ≥ 18(依賴內建的 fetch 和 AbortController)。SDK 只發佈 dist,零執行時依賴,且 sideEffects: false,可以乾淨地做 tree-shaking。請在 Node、Bun、Deno 或邊緣 worker 等可信服務端環境中使用 Connector 和 ProjectConnector。瀏覽器應用應呼叫你的後端,不要把個人或 Project API key 打包進客戶端程式碼。
三個客戶端一覽
Connector | ProjectConnector | OpenConnector | |
|---|---|---|---|
| 驗證 | 個人 api_… key | Project oo_proj_… key | 選用 runtime token oct_… |
| 對接 | OOMOL 託管閘道 | OOMOL 託管閘道 | 你執行的 OpenConnector runtime |
| 帳號資源 | 個人或 Team 的 connections | external users 的 connected accounts | runtime 中的 connections |
| 用途 | 呼叫個人或 Team 已連接的帳號 | 為 SaaS 產品使用者連接並呼叫帳號 | 呼叫自部署 runtime |
| 接入指南 | Connector SDK | ProjectConnector | OpenConnector SDK |
Connector:呼叫個人或 Team connections
取得 API key
你需要一個 OOMOL Connector 的個人 API key,形如 api_…。在 OOMOL Console 建立:
https://console.oomol.com/api-key
把它設為環境變數;本指南統一使用 OOMOL_API_KEY。每個請求都會由閘道授權,並以 Authorization: Bearer <apiKey> 傳送。
個人
api_…key 在個人或 Team connection 上執行 action。代最終使用者連接帳號時,使用 Project key(oo_proj_…)和ProjectConnector;見 ProjectConnector 接入指南。呼叫自部署 runtime 時,使用選用 runtime token(oct_…)和OpenConnector;見 OpenConnector SDK 指南。
快速上手
建構客戶端後即可呼叫 action。下面兩種寫法等價,可任選一種。
import { Connector } from "@oomol-lab/connector";
const oomol = new Connector({ apiKey: process.env.OOMOL_API_KEY! });
// 路徑 1:動態字串。可呼叫任意 action id。
const { threads } = await oomol.execute("gmail.search_threads", { query: "from:boss" });
// 路徑 2:namespace 語法糖。底層仍是同一個呼叫。
const result = await oomol.gmail.search_threads({ query: "is:unread" });
execute 直接回傳 action 的輸出。當你還想要執行中繼資料時,用 executeRaw:
const raw = await oomol.executeRaw("gmail.search_threads", { query: "from:ceo" });
raw.data; // 和 execute() 的返回值相同
raw.executionId; // 服務端分配的執行 id(用於工單支援 / 日誌關聯)
raw.actionId; // 回顯的 action id
raw.message; // 成功響應信封裡的可讀訊息
核心呼叫流程由 execute 和 executeRaw 組成。
Connector 的概念
SDK 模型由五個核心概念組成,驗證、憑證和實際 provider 呼叫都由閘道處理:
| 術語 | 含義 |
|---|---|
| Gateway(閘道) | 這個客戶端對接的 OOMOL Connector 託管服務。它持有憑證、真正發起對 provider 的呼叫,並回傳統一的信封。SDK 在本機不執行任何整合邏輯。 |
| Provider / service | 一個第三方 API(gmail、slack、github、notion……)。它就是 action id 的 <service> 前綴。 |
| Action | provider 上的一個操作,識別為 "<service>.<action>"(例如 gmail.search_threads)。action 由閘道提供,客戶端負責呼叫。 |
| Connection(連接) | 某個 provider 已授權、已儲存的憑證。你從不直接接觸 token;只用 connectionName 指明用哪一個連接。OAuth 和憑證生命週期是閘道的職責。 |
| Team(團隊) | 選用的租戶作用域,以 x-oo-team-name 標頭傳送。 |
同一套詞彙貫穿整個 SDK 介面:action id 始終是 "<service>.<action>",connectionName 用來選擇已儲存的連接;在專案客戶端中,externalUserId 標識你的某個最終使用者。
常見操作
| 想要…… | 用 | 說明 |
|---|---|---|
| 執行一個已建模的 action | execute / executeRaw | 帶型別的一行呼叫。executeRaw 還會回傳 { executionId, actionId, message }。 |
| 呼叫尚未建模為 action 的 endpoint | proxy | 透傳到上游 API,由閘道注入該連接的憑證。 |
| 把 action 餵給 LLM / 建構動態表單 | catalog | 任意 action 或 provider 的執行時 JSON Schema(2020-12)。 |
| 查看已經連了什麼 | apps.list | 唯讀列出你已經建立的連接。 |
| 讓你的使用者連接他們自己的帳號 | ProjectConnector | 一個獨立的、按專案作用域的客戶端,用來代你的最終使用者連接帳號並執行 action。 |
provider 和 action 的覆蓋由閘道提供。目前支援 600+ 個 provider,並持續增加;可透過 oomol.catalog.providers() 在執行時發現。
精確型別(選用)
動態字串路徑對任意 actionId 都能編譯通過。預設情況下,每個 action 使用寬鬆型別(入參和出參都是 Record<string, any>),便於先完成呼叫。需要精確入/出參型別和 JSDoc 時,安裝配套型別套件,並為用到的每個 provider 新增一行 side-effect import:
import { Connector } from "@oomol-lab/connector";
import "@oomol-lab/connector-types/gmail"; // gmail.* 的精確型別 + JSDoc
import "@oomol-lab/connector-types/slack"; // ……以及 slack.*
const oomol = new Connector({ apiKey: process.env.OOMOL_API_KEY! });
await oomol.gmail.search_threads({ query: "from:boss" }); // 入參與出參現在都是精確型別
await oomol.notion.append_block({ pageId, text }); // 沒 import notion → 仍可寬鬆呼叫
npm install -D @oomol-lab/connector-types
精確型別由 @oomol-lab/connector-types 和 side-effect import 提供,專案中不會生成額外檔案。已註冊的 action 會得到字面量補全和精確的入/出參型別;未註冊的會退化為 Record<string, any>。即使型別套件落後於後端,新 action 也可以繼續透過寬鬆型別呼叫。核心執行時與型別套件分別發佈。
需要把
moduleResolution設為bundler、node16或nodenext,子路徑 import(@oomol-lab/connector-types/gmail)才能解析。設定細節見@oomol-lab/connector-types儲存庫。
設定
除 apiKey 外每個欄位都是選用的:
new Connector({
apiKey: process.env.OOMOL_API_KEY!, // 必填
baseUrl: "https://connector.oomol.com/v1", // 預設值
team: "acme", // 預設團隊 → x-oo-team-name
connectionName: "work", // 預設連線(更推薦按呼叫 / using() 設定)
timeoutMs: 30_000, // 預設單次請求超時
maxRetries: 2, // 預設;對 429 / 5xx / 網路錯誤用退避 + 抖動重試
fetch: customFetch, // 用於測試 / 代理 / 鏈路追蹤時注入
});
| 欄位 | 預設值 | 說明 |
|---|---|---|
apiKey | — | 必填。以 Authorization: Bearer <apiKey> 傳送。 |
baseUrl | https://connector.oomol.com/v1 | 透過 client 設定顯式覆寫。 |
team | — | 呼叫在哪個租戶下執行。 |
connectionName | — | 當某個 provider 有多個連接時,選用哪一個。單連接場景可作為客戶端預設值;多連接場景建議按呼叫或用 using() 設定。 |
timeoutMs | 30_000 | 單次請求逾時(毫秒)。 |
maxRetries | 2 | 對 429 / 5xx / 網路錯誤重試,採用指數退避 + 抖動。 |
fetch | 全域 fetch | 為測試、代理 agent 或鏈路追蹤注入自訂 fetch。 |
對於 gmail.send_email、slack.post_message 這類有副作用的 action,除非已經確認該 action 提供可用的幂等機制,否則應傳入 { retries: 0 }。網路錯誤可能發生在 provider 已接受請求之後,自動重試可能造成重複操作。
作用域與按呼叫選項
三個層級按此優先順序解析:按呼叫選項 > using() 作用域 > 客戶端預設值。
using() 回傳一個不可變的、合併了給定預設值的子客戶端;原客戶端不受影響:
const work = oomol.using({ connectionName: "work", team: "acme" });
await work.gmail.search_threads({ query: "label:urgent" }); // 在 "work" / "acme" 下執行
按呼叫選項只作用於本次呼叫,並具有最高優先順序:
await oomol.execute(
"gmail.search_threads",
{ query: "from:ceo" },
{
team: "acme", // 本次呼叫覆蓋團隊
connectionName: "alt", // 本次呼叫選用另一個連線
timeoutMs: 10_000, // 本次呼叫用更緊的超時
retries: 0, // 本次呼叫禁用重試
signal: controller.signal, // 傳入 AbortSignal
},
);
connectionName 按同樣的層級解析。它在傳輸層以 x-oo-connector-alias 標頭攜帶(閘道欄位名為 alias),SDK 介面統一使用 connectionName。
Proxy:直接呼叫上游 endpoint
proxy 可以直接存取上游 API endpoint。閘道會注入所選 connection 的憑證,請求和回應保持上游 API 的原始形態。
// 帶型別的 GET。請求路徑使用 `endpoint` 欄位。
const repos = await oomol.proxy<Array<{ name: string }>>("github", {
endpoint: "/user/repos",
method: "GET",
query: { per_page: 5, sort: "updated" },
});
repos.status; // 上游 HTTP 狀態碼
repos.data.map((r) => r.name);
// 帶 body 和上游 header 的 POST;這些 header 會傳送給 provider。
await oomol.proxy("github", {
endpoint: "/repos/acme/widgets/issues",
method: "POST",
headers: { "X-GitHub-Api-Version": "2022-11-28" },
body: { title: "Tracking issue", labels: ["chore"] },
});
endpoint 既接受路徑(相對於 provider 的 base URL 解析),也接受完整 URL,適用於帶區域域名的 provider,例如 https://eu.posthog.com/api/...。method 是 GET | POST | PUT | PATCH | DELETE 之一。回應是 { status, headers, data }。proxy 的 body 在後端是 strict 的:未知頂層 key 會以 invalid_input 拒絕。
Catalog:檢視 provider 與 action
catalog 提供唯讀執行時中繼資料,可用於動態表單、驗證和 LLM 工具定義。入/出參 schema 使用 JSON Schema(2020-12),與編譯期型別套件相互獨立。
// 列出 provider;可選地在服務端收窄。
const all = await oomol.catalog.providers(); // 全部 provider
const mailish = await oomol.catalog.providers({ q: "mail" }); // 全文搜尋 → ?q=
const some = await oomol.catalog.providers({ service: ["gmail", "slack"] }); // 限定 → ?service=…
// 某個 service 的全部 action。
const actions = await oomol.catalog.actions("gmail");
// 單個 action 的完整後設資料,包含執行時 JSON Schema。
const meta = await oomol.catalog.action("gmail.search_threads");
meta.name; // 可讀名稱
meta.requiredScopes;// 該 action 需要的 OAuth scope
meta.inputSchema; // 入參的 JSON Schema(2020-12)
meta.outputSchema; // 出參的 JSON Schema(2020-12)
每個 provider 攜帶 { service, displayName, iconUrl, homepageUrl, categories, authTypes }。
Apps:列出你已連接的帳號
apps.list() 回傳閘道已持有連接的唯讀檢視。連接建立和移除在 Console 中完成。
const apps = await oomol.apps.list();
for (const app of apps) {
// { id, service, status, connectionName, … };未設定時 connectionName 為 null。
console.log(`${app.service}: id=${app.id} status=${app.status} connectionName=${app.connectionName}`);
}
// 把某個連線的 connectionName 作為按呼叫選擇器傳回,即可指定它。
const work = apps.find((a) => a.connectionName === "work");
if (work) {
await oomol.execute("gmail.search_threads", { query: "is:unread" }, { connectionName: "work" });
}
錯誤處理
失敗會拋出帶型別的 ConnectorError。呼叫方主動取消(被中止的 AbortSignal)會拋出標準 AbortError,可與閘道或傳輸錯誤區分。
import { Connector, ConnectorError, isRetryable } from "@oomol-lab/connector";
try {
await oomol.slack.post_message({ channel: "#general", text: "shipped" });
} catch (err) {
if (err instanceof ConnectorError) {
err.code; // 可判別聯合,例如 "rate_limited"、"credential_expired"
err.status; // HTTP 狀態碼(客戶端 / 網路錯誤時為 0)
err.requestId; // 失敗關聯 id
err.actionId; // 適用時
err.executionId; // 適用時
err.data; // 上游響應體,例如 provider_error 時
if (isRetryable(err)) {
// 429 / 5xx / 網路 / rate_limited / proxy_upstream_timeout / request_in_progress
}
} else {
throw err; // 非 ConnectorError,例如呼叫方取消產生的 AbortError;重新丟擲
}
}
err.code 是一個開放聯合:已知後端碼有自動補全,新的後端碼也會作為字串透傳。處理時建議保留預設分支。常見錯誤碼按組如下:
| 分組 | 錯誤碼 |
|---|---|
| 輸入 / 請求 | invalid_input、invalid_request_payload、invalid_request_signature |
| App / provider | app_not_found、app_not_ready、app_auth_type_mismatch、provider_not_found、provider_not_configured、provider_config_not_found、provider_error、profile_not_found |
| 憑證 / 驗證 | credential_expired、scope_missing、user_oauth_client_required |
| 連接選擇 | connection_ambiguous、connection_account_conflict、connection_alias_conflict、connection_request_not_found、connected_account_not_found |
| Proxy | proxy_not_supported、proxy_upstream_error、proxy_upstream_timeout、proxy_response_too_large |
| 限流 / 並行 | rate_limited、request_in_progress、request_key_conflict、request_key_used |
| 僅客戶端(status 0,請求未發出或傳輸失敗) | client_invalid_request、client_timeout、client_network_error、client_wait_timeout |
isRetryable(err) 對 rate_limited、proxy_upstream_timeout、request_in_progress、HTTP 429、任意 5xx 以及傳輸失敗(status 0)回傳 true。對客戶端驗證錯誤(client_invalid_request)和 waitForConnection 的等待上限(client_wait_timeout)回傳 false;這兩類錯誤通常需要修改呼叫或等待邏輯。
取消與逾時
傳入 AbortSignal 可取消呼叫;設定 timeoutMs 可限定單次呼叫。內建重試層會處理瞬時失敗;需要一次確定性的單次嘗試時,可設定 retries: 0。
const controller = new AbortController();
setTimeout(() => controller.abort(), 50);
try {
await oomol.execute("gmail.search_threads", { query: "huge" }, { signal: controller.signal });
} catch (err) {
(err as Error).name; // "AbortError"
}
ProjectConnector:為產品使用者連接帳號
Connector 在你自己的連接上執行 action。ProjectConnector 面向 SaaS 產品:你的最終使用者透過你的應用連接他們自己的 Gmail / Slack / GitHub / …… 帳號,然後由你的後端代他們執行 action。這屬於託管驗證(managed auth)模型,使用方式類似 Composio 和 Pipedream Connect。
使用 OAuth 時,使用者在閘道託管頁面上授權,provider token 保留在閘道中,你的程式碼只持有不透明識別碼。project.connect.apiKey 和 project.connect.customCredential 的邊界不同:最終使用者的密鑰會先由你的可信後端接收,再傳送給閘道。不要讓這些密鑰進入瀏覽器、提示詞、模型上下文或日誌。
最終使用者識別碼
externalUserId 是專案客戶端的使用者隔離鍵,由你選定,通常使用你自己資料庫裡的使用者 id。連接帳號、等待連接、執行 action 等專案操作都按它作用域。始終傳入一致的 externalUserId,閘道會讓每個使用者的連接彼此隔離。
建構專案客戶端
ProjectConnector 是一個使用 Project API key(oo_proj_…)的獨立客戶端。它提供 connect.*、waitForConnection、getUserProfile、execute、executeRaw 和 forUser 等 Project 作用域操作。
import { ProjectConnector } from "@oomol-lab/connector";
const project = new ProjectConnector({ apiKey: process.env.OOMOL_PROJECT_API_KEY! }); // oo_proj_...
先在 Console 完成設定。 在你的後端連接帳號之前,需要由管理員在 OOMOL Console 建立 Project、服務設定(provider config)和 Project API key。一次性設定和對應的後端 REST 流程見 Connector for SaaS 使用指南。本 SDK 是同一套執行時 API 的帶型別封裝。
OAuth:建立連結,再等待完成
OAuth 分兩步:建立待處理的連接請求,引導使用者授權,再等待完成。
// 1. 為你的某個使用者建立一個待處理的連線請求。
const request = await project.connect.oauth("user_42", {
service: "gmail",
connectionName: "work", // 要賦予的名字;之後用它來指定這個賬號
returnUri: "https://app.example.com/connected", // 回撥完成後閘道器把使用者帶回到這裡
});
// 2. 把使用者帶到 provider 的授權頁面。
redirectUserTo(request.authorizationUrl);
// 3. 輪詢直到使用者完成(或失敗 / 過期)。返回最終的連線請求。
const connected = await project.waitForConnection(request);
connected.status; // "connected" | "failed" | "expired"
connected.connectedAccountId; // 連線成功後儲存的賬號 id
authorizationUrl 會先開啟 Connector 託管的授權入口頁,展示你的專案和使用者將要授權的 provider,然後再把使用者帶到 provider 的 OAuth 頁面。

waitForConnection 會輪詢到請求離開 initiated 狀態後回傳它。如果使用者未完成授權,請求會自然變為 expired。maxWaitMs(預設 600_000 毫秒,與請求過期時間一致)先耗盡時,會拋出錯誤碼為 client_wait_timeout 的 ConnectorError;被中止的 signal 會拋出標準 AbortError。
當你的 returnUri 被命中時,閘道會追加一些 query 參數,你可以在回呼頁面讀取:
status=success
service=gmail
providerConfigId=pc-1
externalUserId=user_42
connectedAccountId=ca-1
……或者在取消 / provider 出錯時:
status=error
code=<connector-error-code>
message=<human-readable-message>
API key / 自訂憑證:同步回傳
API key 和自訂憑證連接會立即回傳帳號;只有 OAuth 需要 waitForConnection。
// 終端使用者自己的上游 key(例如 OpenAI 的 sk-…),不要填寫 oo_proj_ key。
const account = await project.connect.apiKey("user_42", { service: "openai", apiKey: "sk-..." });
account.available; // 該賬號此刻是否可以執行 action
// provider 特有的憑證欄位,由閘道器按 provider config 校驗。
await project.connect.customCredential("user_42", {
service: "jira",
values: { email: "user@acme.com", token: "..." },
});
每個 connect.* 呼叫用 service 或 providerConfigId 中恰好一個來標識 provider。當一個專案對同一 service 有多個設定時用 providerConfigId;簡單情形用 service。
使用者連接的是哪個第三方帳號
getUserProfile 讀取一個已連接帳號背後的第三方帳號主體:你的最終使用者在 provider 上到底是誰。閘道會即時從 provider 拉取,並歸一化成跨 provider 統一的結構。
const { service, profile, fetchedAt } = await project.getUserProfile(account.connectedAccountId);
profile.id; // provider 側穩定的使用者 id
profile.kind; // "user" | "bot" | "service_account" | "unknown"(開放聯合型別)
profile.username; // 使用者名稱/handle,或 null
profile.displayName; // 展示名,或 null
profile.avatarUrl; // 頭像 URL,或 null
profile.email; // 郵箱;已授權的 scope 不包含時為 null
profile.metadata; // 閘道器暴露的 provider 特有欄位
provider 未提供或已授權 scope 未覆蓋的欄位(最常見的是 email)會保留在回應中並設為 null。fetchedAt 是閘道讀取該資料時的 Unix 毫秒時間戳。connectedAccountId 可以來自 connect.apiKey / connect.customCredential 的回傳值,或來自已進入 connected 狀態的 ConnectionRequest。未知 id 會以 connected_account_not_found 拒絕;provider 缺少 userProfile 能力時回傳 profile_not_found;帳號未就緒或不可用時還可能回傳 app_not_ready、app_auth_type_mismatch 或 credential_expired。限定到單一使用者的子客戶端上也有這個方法:user.getUserProfile(connectedAccountId)。
代使用者執行 action
// provider service 從 actionId 字首("gmail")推導。
// 未傳 connectionName / connectedAccountId 時,使用該使用者最新的 active 賬號。
const out = await project.execute(
"user_42",
"gmail.search_threads",
{ query: "is:unread" },
{ connectionName: "work" },
);
帳號選擇優先順序:connectedAccountId(指定某個帳號)優先於 connectionName(按名字選帳號);兩者都不傳時,閘道使用該使用者在該 provider 下最新的 active 帳號。project.executeRaw 回傳與個人客戶端相同的 { data, executionId, actionId, message } 信封。
綁定到單一使用者
forUser 可預先綁定 externalUserId,後續呼叫不必重複傳 id:
const user = project.forUser("user_42");
const request = await user.connect.oauth({ service: "slack" });
const slack = await user.waitForConnection(request);
if (slack.status === "connected") {
await user.execute(
"slack.post_message",
{ channel: "#general", text: "shipped" },
{ connectedAccountId: slack.connectedAccountId },
);
}
project.execute 複用與個人路徑相同的 @oomol-lab/connector-types 註冊表:已 import 的 provider 得到精確的入/出參,其餘保持寬鬆可呼叫。
授權請求與帳號生命週期
| 物件 | 何時拿到 | 關鍵欄位 |
|---|---|---|
ConnectionRequest | 由 connect.oauth 回傳,可經 getConnectionRequest / waitForConnection 重新讀取 | id、status(initiated → connected / failed / expired)、authorizationUrl、connectedAccountId、externalUserId、connectionName、expiresAt |
ConnectedAccount | 由 connect.apiKey / connect.customCredential 同步回傳;OAuth 請求完成後也指向它 | id / connectedAccountId、status(active、reauth_required、error、disconnected)、available、externalUserId、connectionName、service |
只有當執行所需條件都就緒時,available 才為 true:provider config 是 active、帳號是 active、底層 app 是 active,且憑證存在。兩個 status 欄位都是開放聯合;處理時保留預設分支,以相容新的後端狀態。
從 Composio / Pipedream 遷移?
| Composio / Pipedream | @oomol-lab/connector |
|---|---|
userId / external_user_id | externalUserId |
connectedAccounts.initiate / createConnectToken(OAuth) | project.connect.oauth |
connectedAccounts.initiate + AuthScheme.APIKey | project.connect.apiKey |
waitForConnection() | project.waitForConnection() |
tools.execute(slug, { userId, arguments }) | project.execute(externalUserId, actionId, input) |
composio.getEntity(userId) | project.forUser(externalUserId) |
OpenConnector:呼叫自部署 runtime
想自己執行開源的 Connector 服務——在 localhost、Docker,或你自己的基礎設施上?OpenConnector 就是它的個人客戶端。它把 Connector 的介面(兩條呼叫路徑、proxy、catalog、apps)指向你自己執行的伺服器,所以你已經寫好的程式碼幾乎不用改。搭建伺服器是另一個話題,見 OpenConnector 自託管指南。
OpenConnector client 指向一個由你維運的 runtime。帳號組織方式、connection 選擇和存取策略由該 runtime 的設定決定。
import { OpenConnector } from "@oomol-lab/connector";
const open = new OpenConnector(); // 僅用於本地或私有網路;預設 http://localhost:3000
await open.execute("hackernews.get_top_stories", {}); // 路徑 1 — 動態字串
await open.gmail.search_threads({ query: "from:boss" }); // 路徑 2 — namespace 語法糖,登錄檔型別相同
await open.proxy("github", { endpoint: "/user", method: "GET" }); // 路徑 3 — 透傳到尚未建模的 endpoint
await open.catalog.search("send email", { limit: 5 }); // catalog 擴充套件:search、services
await open.apps.list(); // 只讀檢視執行時的連線
兩條呼叫路徑和精確型別工作流都與個人 Connector 一致;同樣的 @oomol-lab/connector-types side-effect import 也適用。OpenConnector 的 proxy.endpoint 接受以 / 開頭的相對路徑;傳入絕對 URL 會回傳 invalid_input。缺少 proxy executor 的 provider 會回傳 proxy_not_supported。
指向你的伺服器
baseUrl 接收伺服器 origin,客戶端會自動新增 API 路徑前綴。未建立 token 的 runtime 只適合 localhost 或其他私有網路。透過公開 URL 暴露前,應先在 Web Console 的 Access 頁面建立 runtime token(oct_…),並要求所有 /v1 和 /mcp client 攜帶它。
const open = new OpenConnector({
baseUrl: "https://connect.internal.example.com", // 伺服器 origin
runtimeToken: process.env.OOMOL_CONNECT_RUNTIME_TOKEN!, // oct_…;公開 runtime 必填
connectionName: "work", // 可選的客戶端級預設連線
});
每個欄位都是選用的。timeoutMs、maxRetries、fetch 的行為與託管客戶端完全一致。
僅執行時才有的介面
OpenConnector 提供以下 runtime 專用介面:
await open.health(); // { ok, runtime } — 連通性 / 鑑權探測
await open.catalog.services(); // 所有有 action 的 service id
await open.catalog.search("top stories", { limit: 3 }); // 按全文相關度對 action 排序
await open.apps.listByService("github"); // 某個 service 的連線
await open.apps.authenticated(["github", "notion"]); // 哪些已存有**真實**憑證
連接選擇分兩層:按呼叫的 connectionName 覆寫客戶端預設值;兩層都省略時,runtime 使用 "default" connection。
管理操作使用 Web Console。 在主控台建立 connection、設定 OAuth client 並生成 runtime token;
OpenConnectorSDK 呼叫已經設定好的 runtime。設定方法見自託管指南。當 service id 與成員名(execute/executeRaw/health/proxy/catalog/apps)衝突時,可以透過execute("<service>.<action>", …)呼叫。
完整可執行範例——examples/open.ts。
參考
Connector(個人 api_… key)
new Connector(config: ClientConfig)
oomol.execute(actionId, input, options?) // → action 輸出
oomol.executeRaw(actionId, input, options?) // → { data, executionId, actionId, message }
oomol.<service>.<action>(input, options?) // execute 的 namespace 語法糖
oomol.using(scope) // → 不可變的作用域子客戶端
oomol.proxy(service, { endpoint, method, query?, headers?, body? }, options?) // → { status, headers, data }
oomol.catalog.action(actionId, options?) // → ActionMetadata
oomol.catalog.actions(service, options?) // → ActionMetadata[]
oomol.catalog.providers(query?, options?) // → ProviderMetadata[] query: { service?: string[]; q?: string }
oomol.apps.list(options?) // → ConnectedApp[]
ProjectConnector(專案 oo_proj_… key)
new ProjectConnector(config: ProjectConnectorConfig)
project.connect.oauth(externalUserId, input, options?) // → ConnectionRequest(待處理)
project.connect.apiKey(externalUserId, input, options?) // → ConnectedAccount(同步)
project.connect.customCredential(externalUserId, input, options?) // → ConnectedAccount(同步)
project.getConnectionRequest(connectionRequestId, options?) // → ConnectionRequest
project.waitForConnection(requestOrId, options?) // → ConnectionRequest options: { pollIntervalMs?, maxWaitMs?, signal?, timeoutMs? }
project.getUserProfile(connectedAccountId, options?) // → ConnectedAccountProfile
project.execute(externalUserId, actionId, input, options?) // → action 輸出
project.executeRaw(externalUserId, actionId, input, options?) // → { data, executionId, actionId, message }
project.forUser(externalUserId) // → ProjectUser(方法相同,id 已繫結)
connect.* 的入參是 { service | providerConfigId } & { connectionName?, … }(service / providerConfigId 恰好取一個)。execute 的選項額外增加 { providerConfigId?, service?, connectedAccountId?, connectionName? }。
OpenConnector(自託管執行時,選用 oct_… token)
new OpenConnector(config?: OpenConnectorConfig) // 每個欄位都可選;baseUrl 預設 http://localhost:3000
open.execute(actionId, input, options?) // → action 輸出
open.executeRaw(actionId, input, options?) // → { data, executionId, actionId, message }
open.<service>.<action>(input, options?) // execute 的 namespace 語法糖
open.health(options?) // → { ok, runtime }
open.proxy(service, { endpoint, method, query?, headers?, body? }, options?) // → { status, headers, data }(endpoint 必須是相對路徑)
open.catalog.action(actionId, options?) // → OpenActionMetadata
open.catalog.actions(service, options?) // → OpenActionMetadata[]
open.catalog.services(options?) // → string[](有 action 的 service id)
open.catalog.providers(query?, options?) // → ProviderMetadata[]
open.catalog.search(q, query?, options?) // → OpenActionSearchResult[] query: { service?, limit? }
open.apps.list(options?) // → ConnectedApp[]
open.apps.listByService(service, options?) // → ConnectedApp[]
open.apps.authenticated(services, options?) // → string[](存有真實憑證的 service)
options 是 { connectionName?, signal?, timeoutMs?, retries? }。config 支援 { baseUrl?, runtimeToken?, connectionName?, timeoutMs?, maxRetries?, fetch? }。
匯出項
import {
Connector,
ProjectConnector,
OpenConnector,
ConnectorError,
isRetryable,
} from "@oomol-lab/connector";
import type {
ClientConfig, CallOptions, ScopeOptions, RawResult,
ProxyRequest, ProxyResponse, ProxyMethod,
CatalogApi, ActionMetadata, ProviderMetadata, ProviderQuery,
AppsApi, ConnectedApp,
ConnectorErrorCode,
ProjectConnectorConfig, ProjectCallOptions, ProjectExecuteOptions,
ConnectionRequest, ConnectedAccount, ProviderSelector,
ConnectedAccountProfile, ProviderUserProfile, ProviderUserKind,
OAuthConnectInput, ApiKeyConnectInput, CustomCredentialConnectInput,
OpenConnectorConfig, OpenConnectorApi, OpenCallOptions, OpenExecuteOptions,
OpenCatalogApi, OpenAppsApi, OpenHealth,
OpenActionMetadata, OpenActionFollowUp, OpenActionAsyncLifecycle,
OpenActionSearchResult, OpenSearchQuery,
} from "@oomol-lab/connector";
可執行且經過型別檢查的範例位於儲存庫的 examples/ 目錄。
授權條款
MIT,見 connector-sdk 儲存庫。
Wanta