(props: {
/** Integration slug — the `<integration>` address segment. */
readonly integration: IntegrationSlug;
/** Bare tool name — the `<tool>` address segment (may contain dots). */
readonly toolName: string;
/** This integration's connections (accounts) across BOTH owners. Each is
* addressed under its OWN owner — there is no ambient owner. */
readonly connections: readonly Connection[];
/** Pre-select this connection (the account the selected tool belongs to in the
* grouped Tools tab). Falls back to the first connection when null/unmatched. */
readonly initialConnectionName?: ConnectionName | string | null;
})
| 88 | }; |
| 89 | |
| 90 | export function ToolRunPanel(props: { |
| 91 | /** Integration slug — the `<integration>` address segment. */ |
| 92 | readonly integration: IntegrationSlug; |
| 93 | /** Bare tool name — the `<tool>` address segment (may contain dots). */ |
| 94 | readonly toolName: string; |
| 95 | /** This integration's connections (accounts) across BOTH owners. Each is |
| 96 | * addressed under its OWN owner — there is no ambient owner. */ |
| 97 | readonly connections: readonly Connection[]; |
| 98 | /** Pre-select this connection (the account the selected tool belongs to in the |
| 99 | * grouped Tools tab). Falls back to the first connection when null/unmatched. */ |
| 100 | readonly initialConnectionName?: ConnectionName | string | null; |
| 101 | }) { |
| 102 | const { integration, toolName, connections, initialConnectionName } = props; |
| 103 | |
| 104 | const [selectedConnection, setSelectedConnection] = useState<ConnectionName | null>( |
| 105 | (initialConnectionName as ConnectionName | null) ?? connections[0]?.name ?? null, |
| 106 | ); |
| 107 | const [argsJson, setArgsJson] = useState("{}"); |
| 108 | const [argsError, setArgsError] = useState<string | null>(null); |
| 109 | const [argsMode, setArgsMode] = useState<"form" | "json">("json"); |
| 110 | const [running, setRunning] = useState(false); |
| 111 | const [result, setResult] = useState<RunResult | null>(null); |
| 112 | |
| 113 | const doExecute = useAtomSet(executeCode, { mode: "promiseExit" }); |
| 114 | |
| 115 | // Reset the picker + result when the connection set or the tool changes, so a |
| 116 | // result never lingers against a stale tool/account. Prefer the tool's own |
| 117 | // account (`initialConnectionName`) when it is present in the list. |
| 118 | useEffect(() => { |
| 119 | setSelectedConnection((current) => { |
| 120 | if ( |
| 121 | initialConnectionName && |
| 122 | connections.some((c: Connection) => c.name === initialConnectionName) |
| 123 | ) { |
| 124 | return initialConnectionName as ConnectionName; |
| 125 | } |
| 126 | if (current && connections.some((c: Connection) => c.name === current)) return current; |
| 127 | return connections[0]?.name ?? null; |
| 128 | }); |
| 129 | }, [connections, initialConnectionName]); |
| 130 | |
| 131 | useEffect(() => { |
| 132 | setResult(null); |
| 133 | setArgsError(null); |
| 134 | }, [toolName]); |
| 135 | |
| 136 | // The full `Connection` for the selected name — carries its OWN owner, which |
| 137 | // is the `<owner>` address segment (never an ambient owner). |
| 138 | const selectedConnectionObj = useMemo<Connection | null>( |
| 139 | () => connections.find((c: Connection) => c.name === selectedConnection) ?? null, |
| 140 | [connections, selectedConnection], |
| 141 | ); |
| 142 | |
| 143 | // Build the per-connection address `tools.<int>.<owner>.<conn>.<tool>` and the |
| 144 | // proxy form (the same string minus the `tools.` root), using the SELECTED |
| 145 | // CONNECTION's own owner. |
| 146 | const addressNoPrefix = useMemo( |
| 147 | () => |
nothing calls this directly
no test coverage detected