From 895e402796ec57977a0099fa7288419c5ad802cd Mon Sep 17 00:00:00 2001 From: hyrious Date: Fri, 30 Oct 2026 18:18:11 +0800 Subject: [PATCH 1/4] feat(connector): support connection names in proxy requests --- docs/commands.md | 3 +++ docs/commands.zh-CN.md | 2 ++ .../commands/connector/index.cli.test.ts | 9 +++++++++ src/application/commands/connector/proxy.ts | 17 +++++++++++++++++ src/application/commands/connector/shared.ts | 6 +++++- .../commands/telemetry-decisions.test.ts | 3 ++- src/i18n/catalog.ts | 4 ++++ 7 files changed, 42 insertions(+), 2 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index 2e6e6c81..d082592b 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1259,6 +1259,9 @@ read-only. Proxy a provider API request through a connected connector app. +- Options: `--connection-name ` selects a named connection within the selected + identity. Leading and trailing whitespace is removed; empty names are rejected. + It works with both `--data` and split request options. - Arguments: `` is the service name. - Options: `-d, --data ` accepts a complete proxy request JSON object or `@path` to a JSON file. The object shape is diff --git a/docs/commands.zh-CN.md b/docs/commands.zh-CN.md index fc0c38e4..a8cc2e13 100644 --- a/docs/commands.zh-CN.md +++ b/docs/commands.zh-CN.md @@ -1044,6 +1044,8 @@ CLI 默认记录受隐私约束的命令使用 telemetry。事件不包含 free- 通过已连接的 connector app 代理 provider API 请求。 +- 选项:`--connection-name ` 选择当前身份下的指定连接。名称会去除首尾 + 空白,空名称会被拒绝。可与 `--data` 或拆分请求选项一起使用。 - 参数:`` 为服务名。 - 选项:`-d, --data ` 接收完整 proxy request JSON object,或使用 `@路径` 读取 JSON 文件。对象形状为 diff --git a/src/application/commands/connector/index.cli.test.ts b/src/application/commands/connector/index.cli.test.ts index 25e97b67..7fe3ad4f 100644 --- a/src/application/commands/connector/index.cli.test.ts +++ b/src/application/commands/connector/index.cli.test.ts @@ -688,6 +688,8 @@ describe("connectorCommand CLI", () => { "{\"accept\":\"application/json\"}", "--body", "{\"query\":\"hello\"}", + "--connection-name", + " private-work ", "--team", "acme", "--json", @@ -741,6 +743,7 @@ describe("connectorCommand CLI", () => { expect(requests[0]?.method).toBe("POST"); expect(requests[0]?.url).toBe("https://connector.oomol.com/v1/proxy/tavily"); expect(requests[0]?.headers.get("x-oo-team-name")).toBe("acme"); + expect(requests[0]?.headers.get("x-oo-connector-alias")).toBe("private-work"); await expect(requests[0]?.json()).resolves.toEqual({ body: { query: "hello", @@ -757,12 +760,14 @@ describe("connectorCommand CLI", () => { expect(telemetryPayload).toMatchObject({ properties: { command_full: "connector.proxy", + connection_selector: "connectionName", data_size_bucket: "<1KB", has_body: true, identity_source: "flag", method: "POST", }, }); + expect(JSON.stringify(telemetryPayload)).not.toContain("private-work"); expect(telemetryPayload?.properties).not.toHaveProperty("body"); expect(telemetryPayload?.properties).not.toHaveProperty("endpoint"); expect(telemetryPayload?.properties).not.toHaveProperty("headers"); @@ -1142,6 +1147,10 @@ describe("connectorCommand CLI", () => { try { const cases = [ + { + argv: ["connector", "proxy", "tavily", "--data", "{\"endpoint\":\"/search\",\"method\":\"GET\"}", "--connection-name", " "], + message: "The --connection-name value cannot be empty.", + }, { argv: ["connector", "proxy", "tavily"], message: "The --endpoint option is required when --data is omitted.", diff --git a/src/application/commands/connector/proxy.ts b/src/application/commands/connector/proxy.ts index 5be0627b..f520ab68 100644 --- a/src/application/commands/connector/proxy.ts +++ b/src/application/commands/connector/proxy.ts @@ -50,6 +50,7 @@ type ConnectorProxyRequest = z.output; interface ConnectorProxyInput { body?: string; + connectionName?: string; data?: string; endpoint?: string; headers?: string; @@ -72,6 +73,12 @@ export const connectorProxyCommand: CliCommandDefinition = }, ], options: [ + { + name: "connectionName", + longFlag: "--connection-name", + valueName: "connection-name", + descriptionKey: "options.connectorProxyConnectionName", + }, { name: "data", longFlag: "--data", @@ -115,6 +122,7 @@ export const connectorProxyCommand: CliCommandDefinition = output: "standard", inputSchema: z.object({ body: z.string().optional(), + connectionName: z.string().optional(), data: z.string().optional(), endpoint: z.string().optional(), headers: z.string().optional(), @@ -127,6 +135,13 @@ export const connectorProxyCommand: CliCommandDefinition = // Payload parsing must stay ahead of the session: proxy usage errors // are reported before any login requirement. const proxyRequest = await buildConnectorProxyRequest(input, context); + const connectionName = input.connectionName?.trim(); + if (input.connectionName !== undefined && connectionName === "") { + throw new CliUserError("errors.connectorRun.connectionNameEmpty", 2); + } + const connectionSelector + = connectionName === undefined ? undefined : { connectionName }; + const { identity, target } = await resolveConnectorSession( { team: input.team, @@ -135,6 +150,7 @@ export const connectorProxyCommand: CliCommandDefinition = ); context.telemetry?.recordProperties({ + connection_selector: connectionSelector === undefined ? "none" : "connectionName", data_size_bucket: bucketTelemetryBytes( Buffer.byteLength(JSON.stringify(proxyRequest)), ), @@ -146,6 +162,7 @@ export const connectorProxyCommand: CliCommandDefinition = try { response = await runConnectorProxy( { + connectionSelector, identity, proxyRequest, serviceName: input.serviceName, diff --git a/src/application/commands/connector/shared.ts b/src/application/commands/connector/shared.ts index c9c3a36c..0db3480b 100644 --- a/src/application/commands/connector/shared.ts +++ b/src/application/commands/connector/shared.ts @@ -416,6 +416,7 @@ export async function runConnectorAction( export async function runConnectorProxy( options: { + connectionSelector?: ConnectorConnectionSelector; identity?: TeamIdentity | undefined; proxyRequest: unknown; serviceName: string; @@ -427,7 +428,10 @@ export async function runConnectorProxy( authorization: options.target.authorization, context, errors: { scope: "connectorProxy" }, - headers: teamIdentityHeaders(options.identity), + headers: { + ...connectorConnectionSelectorHeaders(options.connectionSelector, options.target.kind), + ...teamIdentityHeaders(options.identity), + }, host: { baseUrl: options.target.baseUrl }, jsonBody: options.proxyRequest, label: "Connector proxy", diff --git a/src/application/commands/telemetry-decisions.test.ts b/src/application/commands/telemetry-decisions.test.ts index f8f96d4f..2095fbfa 100644 --- a/src/application/commands/telemetry-decisions.test.ts +++ b/src/application/commands/telemetry-decisions.test.ts @@ -198,6 +198,7 @@ const commandTelemetryDecisions = { "connector.proxy": { kind: "properties", properties: [ + "connection_selector", "connector_kind", "data_size_bucket", "error_code", @@ -206,7 +207,7 @@ const commandTelemetryDecisions = { "identity_source", "method", ], - reason: "Records connector proxy bucketed payload size, method enum, identity source (none/flag/env_id/env_name/account), stable error code, and HTTP status without service name, endpoint, headers, body, or team name/id.", + reason: "Records connector proxy none/connectionName selector mode without the connection name value, bucketed payload size, method enum, identity source (none/flag/env_id/env_name/account), stable error code, and HTTP status without service name, endpoint, headers, body, or team name/id.", }, "connector.search": { kind: "properties", diff --git a/src/i18n/catalog.ts b/src/i18n/catalog.ts index 40d87052..7747b9dc 100644 --- a/src/i18n/catalog.ts +++ b/src/i18n/catalog.ts @@ -999,6 +999,8 @@ export const enMessages = { "Submit an async action and wait for its result action", "options.connectorRunConnectionName": "Run the action with the connector app connection name", + "options.connectorProxyConnectionName": + "Run the proxy request with the connector app connection name", "options.connectorAppsTeam": "List connected apps under the given team identity", "options.searchAllTeams": "Search all your teams (ignores the current team; conflicts with --team; hosted only)", @@ -2388,6 +2390,8 @@ export const zhMessages = { "提交异步 action,并等待它的结果 action", "options.connectorRunConnectionName": "使用指定 connector app 连接名称运行该 action", + "options.connectorProxyConnectionName": + "使用指定 connector app 连接名称运行该 proxy 请求", "options.connectorAppsTeam": "以指定团队身份列出已连接的 app", "options.searchAllTeams": "搜索所属的全部团队(忽略当前团队,与 --team 互斥,仅限托管服务)", From d19014ea08851aa143a53ded6c1aa8677ecaeb1c Mon Sep 17 00:00:00 2001 From: hyrious Date: Fri, 30 Oct 2026 18:28:05 +0800 Subject: [PATCH 2/4] refactor(connector): share connection selector validation --- .../commands/connector/apps.test.ts | 22 ----------------- src/application/commands/connector/proxy.ts | 9 ++----- src/application/commands/connector/run.ts | 22 +++++++---------- src/application/commands/connector/shared.ts | 24 +++++++++++++++++++ 4 files changed, 34 insertions(+), 43 deletions(-) diff --git a/src/application/commands/connector/apps.test.ts b/src/application/commands/connector/apps.test.ts index 5de5183a..ad9e78e5 100644 --- a/src/application/commands/connector/apps.test.ts +++ b/src/application/commands/connector/apps.test.ts @@ -88,28 +88,6 @@ describe("formatConnectorAppsAsText", () => { displayWidthBeforeStatus(lines[2]!), ); }); - - test("returns the no-connections message for an empty all-scope listing", () => { - const output = formatConnectorAppsAsText( - [], - "all", - createTranslatorStub(), - createTerminalColors(false), - ); - - expect(output).toBe("connector.apps.text.noConnections"); - }); - - test("returns the per-service no-results message for an empty service-scope listing", () => { - const output = formatConnectorAppsAsText( - [], - "service", - createTranslatorStub(), - createTerminalColors(false), - ); - - expect(output).toBe("connector.apps.text.noResults"); - }); }); function sampleApp(overrides: Partial = {}): ConnectorAppRow { diff --git a/src/application/commands/connector/proxy.ts b/src/application/commands/connector/proxy.ts index f520ab68..df2ee408 100644 --- a/src/application/commands/connector/proxy.ts +++ b/src/application/commands/connector/proxy.ts @@ -14,7 +14,7 @@ import { } from "../team/identity.ts"; import { formatConnectorExecutionResultAsText } from "./result-text.ts"; import { resolveConnectorSession } from "./session.ts"; -import { runConnectorProxy } from "./shared.ts"; +import { parseConnectorConnectionSelector, runConnectorProxy } from "./shared.ts"; import { recordConnectorFailureTelemetry } from "./telemetry.ts"; const connectorProxyDataErrorKeys = { @@ -135,12 +135,7 @@ export const connectorProxyCommand: CliCommandDefinition = // Payload parsing must stay ahead of the session: proxy usage errors // are reported before any login requirement. const proxyRequest = await buildConnectorProxyRequest(input, context); - const connectionName = input.connectionName?.trim(); - if (input.connectionName !== undefined && connectionName === "") { - throw new CliUserError("errors.connectorRun.connectionNameEmpty", 2); - } - const connectionSelector - = connectionName === undefined ? undefined : { connectionName }; + const connectionSelector = parseConnectorConnectionSelector(input); const { identity, target } = await resolveConnectorSession( { diff --git a/src/application/commands/connector/run.ts b/src/application/commands/connector/run.ts index 604887f2..4f756818 100644 --- a/src/application/commands/connector/run.ts +++ b/src/application/commands/connector/run.ts @@ -25,6 +25,7 @@ import { } from "./schema-cache.ts"; import { resolveConnectorSession } from "./session.ts"; import { + parseConnectorConnectionSelector, requireConnectorActionName, runConnectorAction, } from "./shared.ts"; @@ -147,21 +148,14 @@ export const connectorRunCommand: CliCommandDefinition = { throw new CliUserError("errors.connectorRun.waitModeConflict", 2); } - const connectionName = input.connectionName?.trim(); - if (input.connectionName !== undefined && connectionName === "") { - throw new CliUserError("errors.connectorRun.connectionNameEmpty", 2); + const connectionSelector = parseConnectorConnectionSelector(input); + let selectorMode: "none" | "appId" | "connectionName" = "none"; + if (connectionSelector?.appId !== undefined) { + selectorMode = "appId"; } - - const appId = input.appId?.trim(); - if (input.appId !== undefined && appId === "") { - throw new CliUserError("errors.connectorRun.appIdEmpty", 2); - } - if (appId !== undefined && connectionName !== undefined) { - throw new CliUserError("errors.connectorRun.connectionSelectorConflict", 2); + else if (connectionSelector?.connectionName !== undefined) { + selectorMode = "connectionName"; } - const connectionSelector = appId !== undefined - ? { appId } - : connectionName === undefined ? undefined : { connectionName }; const { identity, target } = await resolveConnectorSession( { @@ -182,7 +176,7 @@ export const connectorRunCommand: CliCommandDefinition = { context.telemetry?.recordProperties({ action: actionName, - connection_selector: appId !== undefined ? "appId" : connectionName !== undefined ? "connectionName" : "none", + connection_selector: selectorMode, data_size_bucket: bucketTelemetryBytes( Buffer.byteLength(JSON.stringify(inputData)), ), diff --git a/src/application/commands/connector/shared.ts b/src/application/commands/connector/shared.ts index 0db3480b..7aad88f2 100644 --- a/src/application/commands/connector/shared.ts +++ b/src/application/commands/connector/shared.ts @@ -182,6 +182,30 @@ export interface ConnectorConnectionSelector { connectionName?: string; } +export function parseConnectorConnectionSelector( + input: ConnectorConnectionSelector, +): ConnectorConnectionSelector | undefined { + const connectionName = input.connectionName?.trim(); + if (connectionName === "") { + throw new CliUserError("errors.connectorRun.connectionNameEmpty", 2); + } + + const appId = input.appId?.trim(); + if (appId === "") { + throw new CliUserError("errors.connectorRun.appIdEmpty", 2); + } + if (appId !== undefined && connectionName !== undefined) { + throw new CliUserError("errors.connectorRun.connectionSelectorConflict", 2); + } + if (appId !== undefined) { + return { appId }; + } + if (connectionName !== undefined) { + return { connectionName }; + } + return undefined; +} + export function requireConnectorActionName(rawAction: string | undefined): string { const trimmed = rawAction?.trim(); From 0527696b61bd14c59165418987e0ca59f2a49de0 Mon Sep 17 00:00:00 2001 From: hyrious Date: Fri, 30 Oct 2026 18:31:38 +0800 Subject: [PATCH 3/4] refactor(connector): align option and documentation ordering --- docs/commands.md | 14 +++++------ docs/commands.zh-CN.md | 10 ++++---- src/application/commands/connector/proxy.ts | 12 ++++----- src/application/commands/connector/run.ts | 12 ++++----- src/i18n/catalog.ts | 28 ++++++++++----------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index d082592b..6e70c906 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1166,16 +1166,16 @@ Validate input data and run one connector action. - Options: `-a, --action ` selects the action name and is required. - Options: `-d, --data ` accepts inline JSON or `@path` to a JSON file. `--input ` is an alias for `--data `. -- Options: `--approval` declares that the user has authorized this operation. - The caller assesses whether existing authorization covers the action and its - parameters in context. Ask the user only when authorization is insufficient. -- Options: `--dry-run` validates the payload without executing the action. - Options: `--app-id ` selects the exact connection from search. It is mutually exclusive with `--connection-name`; empty IDs are rejected. The selected team must own that connection; an app ID does not switch teams. - Options: `--connection-name ` runs the action with the connector app connection name. Use `oo connector apps ` to list available connection names. +- Options: `--approval` declares that the user has authorized this operation. + The caller assesses whether existing authorization covers the action and its + parameters in context. Ask the user only when authorization is insufficient. +- Options: `--dry-run` validates the payload without executing the action. - Options: `--wait` polls the selected action until it reaches a terminal state. This option is only valid when the selected action schema declares an async result lifecycle. @@ -1259,9 +1259,6 @@ read-only. Proxy a provider API request through a connected connector app. -- Options: `--connection-name ` selects a named connection within the selected - identity. Leading and trailing whitespace is removed; empty names are rejected. - It works with both `--data` and split request options. - Arguments: `` is the service name. - Options: `-d, --data ` accepts a complete proxy request JSON object or `@path` to a JSON file. The object shape is @@ -1284,6 +1281,9 @@ Proxy a provider API request through a connected connector app. options. - Options: `--body` is parsed as JSON. To send a text body, pass a JSON string such as `"hello"`. +- Options: `--connection-name ` selects a named connection within the selected + identity. Leading and trailing whitespace is removed; empty names are rejected. + It works with both `--data` and split request options. - Options: `--team ` runs the proxy request under the given team identity. When omitted, the request runs under the team selected by `OO_TEAM_ID` / `OO_TEAM_NAME` if set, then the active account's default team, diff --git a/docs/commands.zh-CN.md b/docs/commands.zh-CN.md index a8cc2e13..b2a03755 100644 --- a/docs/commands.zh-CN.md +++ b/docs/commands.zh-CN.md @@ -973,13 +973,13 @@ CLI 默认记录受隐私约束的命令使用 telemetry。事件不包含 free- - 选项:`-a, --action ` 用于指定 action 名称,且为必填。 - 选项:`-d, --data ` 支持直接传入 JSON,或使用 `@路径` 读取 JSON 文件。 `--input ` 是 `--data ` 的 alias。 -- 选项:`--approval` 声明本次操作已有用户授权。调用方结合具体操作、参数、上下文 - 和用户已有指示,判断授权是否充分;不足时再询问用户。 -- 选项:`--dry-run` 只做 payload 校验,不执行 action。 - 选项:`--app-id ` 精确选择搜索返回的连接,与 `--connection-name` 互斥, 不接受空值。当前选择的团队必须拥有该连接,app ID 本身不会切换团队。 - 选项:`--connection-name ` 使用指定 connector app 连接名称 运行该 action。可用 `oo connector apps ` 查看可用连接名称。 +- 选项:`--approval` 声明本次操作已有用户授权。调用方结合具体操作、参数、上下文 + 和用户已有指示,判断授权是否充分;不足时再询问用户。 +- 选项:`--dry-run` 只做 payload 校验,不执行 action。 - 选项:`--wait` 会轮询选中的 action,直到进入终态。只有选中 action 的 schema 声明了异步结果 lifecycle 时,这个选项才有效。 - 选项:`--wait-result` 会提交异步 submit action,然后轮询它配置的结果 @@ -1044,8 +1044,6 @@ CLI 默认记录受隐私约束的命令使用 telemetry。事件不包含 free- 通过已连接的 connector app 代理 provider API 请求。 -- 选项:`--connection-name ` 选择当前身份下的指定连接。名称会去除首尾 - 空白,空名称会被拒绝。可与 `--data` 或拆分请求选项一起使用。 - 参数:`` 为服务名。 - 选项:`-d, --data ` 接收完整 proxy request JSON object,或使用 `@路径` 读取 JSON 文件。对象形状为 @@ -1066,6 +1064,8 @@ CLI 默认记录受隐私约束的命令使用 telemetry。事件不包含 free- credential。 - 选项:`--body` 会按 JSON 解析。如需发送文本 body,请传 JSON string,例如 `"hello"`。 +- 选项:`--connection-name ` 选择当前身份下的指定连接。名称会去除首尾 + 空白,空名称会被拒绝。可与 `--data` 或拆分请求选项一起使用。 - 选项:`--team ` 以指定团队身份运行该 proxy 请求。省略时,若设置了 `OO_TEAM_ID` / `OO_TEAM_NAME` 则使用 env 选定的团队,其次使用 当前账号保存的默认团队,否则使用服务端默认团队。 diff --git a/src/application/commands/connector/proxy.ts b/src/application/commands/connector/proxy.ts index df2ee408..84c8c1dd 100644 --- a/src/application/commands/connector/proxy.ts +++ b/src/application/commands/connector/proxy.ts @@ -73,12 +73,6 @@ export const connectorProxyCommand: CliCommandDefinition = }, ], options: [ - { - name: "connectionName", - longFlag: "--connection-name", - valueName: "connection-name", - descriptionKey: "options.connectorProxyConnectionName", - }, { name: "data", longFlag: "--data", @@ -117,6 +111,12 @@ export const connectorProxyCommand: CliCommandDefinition = valueName: "body", descriptionKey: "options.connectorProxyBody", }, + { + name: "connectionName", + longFlag: "--connection-name", + valueName: "connection-name", + descriptionKey: "options.connectorProxyConnectionName", + }, teamOption("options.connectorProxyTeam"), ], output: "standard", diff --git a/src/application/commands/connector/run.ts b/src/application/commands/connector/run.ts index 4f756818..87ce93d1 100644 --- a/src/application/commands/connector/run.ts +++ b/src/application/commands/connector/run.ts @@ -79,12 +79,6 @@ export const connectorRunCommand: CliCommandDefinition = { }, ], options: [ - { - name: "appId", - longFlag: "--app-id", - valueName: "app-id", - descriptionKey: "options.connectorRunAppId", - }, { name: "action", longFlag: "--action", @@ -100,6 +94,12 @@ export const connectorRunCommand: CliCommandDefinition = { valueName: "data", descriptionKey: "options.data", }, + { + name: "appId", + longFlag: "--app-id", + valueName: "app-id", + descriptionKey: "options.connectorRunAppId", + }, { name: "connectionName", longFlag: "--connection-name", diff --git a/src/i18n/catalog.ts b/src/i18n/catalog.ts index 7747b9dc..176c1283 100644 --- a/src/i18n/catalog.ts +++ b/src/i18n/catalog.ts @@ -420,8 +420,10 @@ export const enMessages = { "The connector action metadata request failed: {message}", "errors.connectorMetadata.requestFailed": "The connector action metadata request returned HTTP {status}.", + "errors.connectorRun.appIdEmpty": "--app-id must not be empty.", "errors.connectorRun.actionRequired": "The --action option is required.", + "errors.connectorRun.connectionSelectorConflict": "--app-id cannot be combined with --connection-name.", "errors.connectorRun.connectionNameEmpty": "The --connection-name value cannot be empty.", "errors.connectorRun.dataFilePathRequired": @@ -510,6 +512,8 @@ export const enMessages = { "Invalid action id \"{actionId}\". Use the form ., for example cal.create_schedule.", "errors.connectorSchema.legacyActionSingleService": "The --action option accepts exactly one bare service name. To look up one or more actions by identifier, omit --action and pass . arguments instead.", + "errors.connectorSearch.teamConflict": "--all-teams cannot be combined with --team.", + "errors.connectorSearch.allTeamsUnsupported": "--all-teams requires the OOMOL-hosted connector and a user identity.", "errors.connectorSearch.invalidResponse": "The connector action search response body is unsupported.", "errors.connectorSearch.requestError": @@ -997,18 +1001,12 @@ export const enMessages = { "Poll until an async result action reaches a terminal state", "options.connectorRunWaitResult": "Submit an async action and wait for its result action", + "options.connectorRunAppId": "Run with the exact app ID returned by search; conflicts with --connection-name", "options.connectorRunConnectionName": "Run the action with the connector app connection name", - "options.connectorProxyConnectionName": - "Run the proxy request with the connector app connection name", "options.connectorAppsTeam": "List connected apps under the given team identity", "options.searchAllTeams": "Search all your teams (ignores the current team; conflicts with --team; hosted only)", - "options.connectorRunAppId": "Run with the exact app ID returned by search; conflicts with --connection-name", - "errors.connectorSearch.teamConflict": "--all-teams cannot be combined with --team.", - "errors.connectorSearch.allTeamsUnsupported": "--all-teams requires the OOMOL-hosted connector and a user identity.", - "errors.connectorRun.appIdEmpty": "--app-id must not be empty.", - "errors.connectorRun.connectionSelectorConflict": "--app-id cannot be combined with --connection-name.", "connector.search.text.team": "Team", "connector.search.text.appId": "App ID", "options.searchTeam": @@ -1021,6 +1019,8 @@ export const enMessages = { "Runtime API token for the self-hosted connector (created on the server's /access page)", "options.connectorProxyBody": "Specify the upstream request body as JSON", + "options.connectorProxyConnectionName": + "Run the proxy request with the connector app connection name", "options.connectorProxyData": "Provide the full proxy request as JSON or @path to a JSON file", "options.connectorProxyEndpoint": @@ -1816,8 +1816,10 @@ export const zhMessages = { "获取 connector action 元数据失败:{message}", "errors.connectorMetadata.requestFailed": "获取 connector action 元数据返回了 HTTP {status}。", + "errors.connectorRun.appIdEmpty": "--app-id 不能为空。", "errors.connectorRun.actionRequired": "--action 选项为必填。", + "errors.connectorRun.connectionSelectorConflict": "--app-id 不能与 --connection-name 同时使用。", "errors.connectorRun.connectionNameEmpty": "--connection-name 的值不能为空。", "errors.connectorRun.dataFilePathRequired": @@ -1906,6 +1908,8 @@ export const zhMessages = { "无效的 action id “{actionId}”。请使用 . 形式,例如 cal.create_schedule。", "errors.connectorSchema.legacyActionSingleService": "--action 选项只接受一个纯服务名(不含点号)。若要按标识查询一个或多个 action,请省略 --action 并改用 . 形式的参数。", + "errors.connectorSearch.teamConflict": "--all-teams 不能与 --team 同时使用。", + "errors.connectorSearch.allTeamsUnsupported": "--all-teams 需要 OOMOL 托管 connector 和用户身份。", "errors.connectorSearch.invalidResponse": "connector action 搜索返回了不受支持的响应内容。", "errors.connectorSearch.requestError": @@ -2388,18 +2392,12 @@ export const zhMessages = { "轮询异步结果 action,直到进入终态", "options.connectorRunWaitResult": "提交异步 action,并等待它的结果 action", + "options.connectorRunAppId": "使用搜索返回的精确 app ID 执行,与 --connection-name 互斥", "options.connectorRunConnectionName": "使用指定 connector app 连接名称运行该 action", - "options.connectorProxyConnectionName": - "使用指定 connector app 连接名称运行该 proxy 请求", "options.connectorAppsTeam": "以指定团队身份列出已连接的 app", "options.searchAllTeams": "搜索所属的全部团队(忽略当前团队,与 --team 互斥,仅限托管服务)", - "options.connectorRunAppId": "使用搜索返回的精确 app ID 执行,与 --connection-name 互斥", - "errors.connectorSearch.teamConflict": "--all-teams 不能与 --team 同时使用。", - "errors.connectorSearch.allTeamsUnsupported": "--all-teams 需要 OOMOL 托管 connector 和用户身份。", - "errors.connectorRun.appIdEmpty": "--app-id 不能为空。", - "errors.connectorRun.connectionSelectorConflict": "--app-id 不能与 --connection-name 同时使用。", "connector.search.text.team": "团队", "connector.search.text.appId": "应用 ID", "options.searchTeam": @@ -2412,6 +2410,8 @@ export const zhMessages = { "自部署 Connector 的 Runtime API 令牌(在服务的 /access 页面创建)", "options.connectorProxyBody": "以 JSON 指定上游请求体", + "options.connectorProxyConnectionName": + "使用指定 connector app 连接名称运行该 proxy 请求", "options.connectorProxyData": "提供完整 proxy request JSON,或使用 @路径读取 JSON 文件", "options.connectorProxyEndpoint": From 3b229167f08eb8b6c20a50f653a40c0a6247b02d Mon Sep 17 00:00:00 2001 From: hyrious Date: Fri, 30 Oct 2026 18:34:14 +0800 Subject: [PATCH 4/4] refactor(connector): align input fields with command options --- src/application/commands/connector/apps.ts | 4 ++-- src/application/commands/connector/login.ts | 4 ++-- src/application/commands/connector/proxy.ts | 20 ++++++++++---------- src/application/commands/connector/run.ts | 20 ++++++++++---------- src/application/commands/connector/schema.ts | 4 ++-- src/application/commands/connector/search.ts | 6 +++--- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/application/commands/connector/apps.ts b/src/application/commands/connector/apps.ts index cdc178d0..31085cde 100644 --- a/src/application/commands/connector/apps.ts +++ b/src/application/commands/connector/apps.ts @@ -34,8 +34,8 @@ const connectorAppStatusColors = { type ConnectorAppsListScope = "all" | "service"; interface ConnectorAppsInput { - team?: string; serviceName?: string; + team?: string; } interface ConnectorAppListItem { @@ -65,8 +65,8 @@ export const connectorAppsCommand: CliCommandDefinition = { ], output: "standard", inputSchema: z.object({ - ...teamIdentityInputShape, serviceName: z.string().optional(), + ...teamIdentityInputShape, }), handler: async (input, context) => { const serviceName = input.serviceName?.trim(); diff --git a/src/application/commands/connector/login.ts b/src/application/commands/connector/login.ts index 2192fac9..9a24af64 100644 --- a/src/application/commands/connector/login.ts +++ b/src/application/commands/connector/login.ts @@ -23,8 +23,8 @@ const connectorHealthResponseSchema = z.object({ }).passthrough(); interface ConnectorLoginInput { - token?: string; url: string; + token?: string; } export const connectorLoginCommand: CliCommandDefinition = { @@ -48,8 +48,8 @@ export const connectorLoginCommand: CliCommandDefinition = }, ], inputSchema: z.object({ - token: z.string().optional(), url: z.string(), + token: z.string().optional(), }), handler: async (input, context) => { const baseUrl = normalizeSelfHostedConnectorUrl(input.url); diff --git a/src/application/commands/connector/proxy.ts b/src/application/commands/connector/proxy.ts index 84c8c1dd..734ae032 100644 --- a/src/application/commands/connector/proxy.ts +++ b/src/application/commands/connector/proxy.ts @@ -49,15 +49,15 @@ const proxyRequestSchema = z.object({ type ConnectorProxyRequest = z.output; interface ConnectorProxyInput { - body?: string; - connectionName?: string; + serviceName: string; data?: string; endpoint?: string; - headers?: string; method?: string; - team?: string; query?: string; - serviceName: string; + headers?: string; + body?: string; + connectionName?: string; + team?: string; } export const connectorProxyCommand: CliCommandDefinition = { @@ -121,15 +121,15 @@ export const connectorProxyCommand: CliCommandDefinition = ], output: "standard", inputSchema: z.object({ - body: z.string().optional(), - connectionName: z.string().optional(), + serviceName: z.string(), data: z.string().optional(), endpoint: z.string().optional(), - headers: z.string().optional(), method: z.string().optional(), - ...teamIdentityInputShape, query: z.string().optional(), - serviceName: z.string(), + headers: z.string().optional(), + body: z.string().optional(), + connectionName: z.string().optional(), + ...teamIdentityInputShape, }), handler: async (input, context) => { // Payload parsing must stay ahead of the session: proxy usage errors diff --git a/src/application/commands/connector/run.ts b/src/application/commands/connector/run.ts index 87ce93d1..162a835a 100644 --- a/src/application/commands/connector/run.ts +++ b/src/application/commands/connector/run.ts @@ -54,16 +54,16 @@ type ConnectorActionAsyncResultLifecycle = Extract< const connectorAsyncLifecycleDefaultTimeoutMs = 6 * 3_600_000; interface ConnectorRunInput { - appId?: string; + serviceName: string; action?: string; - approval?: boolean; - connectionName?: string; data?: string; + appId?: string; + connectionName?: string; + approval?: boolean; dryRun?: boolean; - team?: string; - serviceName: string; wait?: boolean; waitResult?: boolean; + team?: string; } export const connectorRunCommand: CliCommandDefinition = { @@ -130,16 +130,16 @@ export const connectorRunCommand: CliCommandDefinition = { ], output: "standard", inputSchema: z.object({ - appId: z.string().optional(), + serviceName: z.string(), action: z.string().optional(), - approval: z.boolean().optional(), - connectionName: z.string().optional(), data: z.string().optional(), + appId: z.string().optional(), + connectionName: z.string().optional(), + approval: z.boolean().optional(), dryRun: z.boolean().optional(), - ...teamIdentityInputShape, - serviceName: z.string(), wait: z.boolean().optional(), waitResult: z.boolean().optional(), + ...teamIdentityInputShape, }), handler: async (input, context) => { const actionName = requireConnectorActionName(input.action); diff --git a/src/application/commands/connector/schema.ts b/src/application/commands/connector/schema.ts index de77b3a6..9ccf6a70 100644 --- a/src/application/commands/connector/schema.ts +++ b/src/application/commands/connector/schema.ts @@ -10,8 +10,8 @@ import { requireConnectorActionName } from "./shared.ts"; import { resolveConnectorTarget } from "./target.ts"; interface ConnectorSchemaInput { - action?: string; actionId?: string[]; + action?: string; refresh?: boolean; } @@ -52,8 +52,8 @@ export const connectorSchemaCommand: CliCommandDefinition ], output: "json-only", inputSchema: z.object({ - action: z.string().optional(), actionId: z.array(z.string()).optional(), + action: z.string().optional(), refresh: z.boolean().optional(), }), handler: async (input, context) => { diff --git a/src/application/commands/connector/search.ts b/src/application/commands/connector/search.ts index 8e439376..3c739fe1 100644 --- a/src/application/commands/connector/search.ts +++ b/src/application/commands/connector/search.ts @@ -20,9 +20,9 @@ import { resolveConnectorSession } from "./session.ts"; import { resolveConnectorTarget } from "./target.ts"; interface ConnectorSearchInput { + text: string; allTeams?: boolean; team?: string; - text: string; } export const connectorSearchCommand: CliCommandDefinition = { @@ -47,9 +47,9 @@ export const connectorSearchCommand: CliCommandDefinition ], output: "standard", inputSchema: z.object({ - ...teamIdentityInputShape, - allTeams: z.boolean().optional(), text: z.string(), + allTeams: z.boolean().optional(), + ...teamIdentityInputShape, }), handler: async (input, context) => { context.telemetry?.recordProperties({